Skip to content

Commit 32cd314

Browse files
authored
Merge branch 'master' into add_missing_sample_config_tests_on_master
2 parents 95f349e + 5400a27 commit 32cd314

25 files changed

Lines changed: 91 additions & 85 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,30 @@ on:
1313
jobs:
1414
build:
1515
name: Python==${{ matrix.python-version }} | ${{ matrix.django-version }}
16-
runs-on: ubuntu-22.04
16+
runs-on: ubuntu-24.04
1717

1818
strategy:
1919
fail-fast: false
2020
matrix:
2121
python-version:
22-
- "3.8"
2322
- "3.9"
2423
- "3.10"
24+
- "3.11"
25+
- "3.12"
26+
- "3.13"
2527
django-version:
26-
- django~=3.2.0
27-
- django~=4.1.0
2828
- django~=4.2.0
29+
- django~=5.1.0
30+
- django~=5.2.0
31+
exclude:
32+
# Django 5.1+ requires Python >=3.10
33+
- python-version: "3.9"
34+
django-version: django~=5.1.0
35+
- python-version: "3.9"
36+
django-version: django~=5.2.0
37+
# Python 3.13 supported only in Django >=5.1.3
38+
- python-version: "3.13"
39+
django-version: django~=4.2.0
2940

3041
steps:
3142
- uses: actions/checkout@v4

docker-compose.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# NOTE: This Docker image is for development purposes only.
22

3-
version: "3"
4-
53
services:
64
controller:
75
image: openwisp/controller-development:latest
@@ -21,7 +19,7 @@ services:
2119
entrypoint: redis-server --appendonly yes
2220

2321
postgres:
24-
image: postgis/postgis:13-3.3-alpine
22+
image: postgis/postgis:17-3.5-alpine
2523
environment:
2624
POSTGRES_PASSWORD: openwisp2
2725
POSTGRES_USER: openwisp2

docs/developer/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Developer Installation Instructions
1010
Dependencies
1111
------------
1212

13-
- Python >= 3.8
13+
- Python >= 3.9
1414
- OpenSSL
1515

1616
Installing for Development

openwisp_controller/config/base/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def get_config(self):
8989
return config
9090
c = deepcopy(config)
9191
is_config = not any([self.__template__, self.__vpn__])
92-
if 'hostname' not in c.get('general', {}) and is_config:
92+
if all(('hostname' not in c.get('general', {}), is_config, self.name)):
9393
c.setdefault('general', {})
9494
c['general']['hostname'] = self.name.replace(':', '-')
9595
return c

openwisp_controller/config/base/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ def _should_use_dsa(self):
423423

424424
# Check if the device is using stock OpenWrt.
425425
openwrt_match = re.search(
426-
'[oO][pP][eE][nN][wW][rR][tT]\s*([\d.]+)', self.device.os
426+
r'[oO][pP][eE][nN][wW][rR][tT]\s*([\d.]+)', self.device.os
427427
)
428428
if openwrt_match:
429429
if version.parse(openwrt_match.group(1)) >= version.parse('21'):

openwisp_controller/config/base/vpn.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from copy import deepcopy
77
from subprocess import CalledProcessError, TimeoutExpired
88

9-
import django
109
import shortuuid
1110
from cache_memoize import cache_memoize
1211
from django.core.cache import cache
@@ -856,13 +855,9 @@ def register_auto_ip_stopper(cls, func):
856855
cls._auto_ip_stopper_funcs.append(func)
857856

858857
def _get_unique_checks(self, exclude=None, include_meta_constraints=False):
859-
if django.VERSION < (4, 1):
860-
# TODO: Remove when dropping support for Django 3.2
861-
unique_checks, date_checks = super()._get_unique_checks(exclude)
862-
else:
863-
unique_checks, date_checks = super()._get_unique_checks(
864-
exclude, include_meta_constraints
865-
)
858+
unique_checks, date_checks = super()._get_unique_checks(
859+
exclude, include_meta_constraints
860+
)
866861

867862
if not self.vpn._vxlan_vni:
868863
# If VNI is not specified in VXLAN tunnel configuration,

openwisp_controller/config/tests/test_config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ def test_auto_hostname(self):
333333
c.refresh_from_db()
334334
self.assertDictEqual(c.config, {'general': {}})
335335

336+
with self.subTest('missing name shall not raise exception'):
337+
c.device.name = None
338+
del c.backend_instance
339+
self.assertDictEqual(c.backend_instance.config, {'general': {}})
340+
336341
def test_config_context(self):
337342
config = {
338343
'general': {

openwisp_controller/config/tests/test_controller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ def test_ip_fields_not_duplicated(self):
13461346
self.assertIsNone(c1.device.management_ip)
13471347
self.assertEqual(c2.device.management_ip, '192.168.1.99')
13481348
# other organization is not affected
1349-
self.assertEquals(c3.device.last_ip, '127.0.0.1')
1349+
self.assertEqual(c3.device.last_ip, '127.0.0.1')
13501350
self.assertEqual(c3.device.management_ip, '192.168.1.99')
13511351

13521352
with self.subTest('test interaction with DeviceChecksumView caching'):

openwisp_controller/config/tests/test_template.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
from openwisp_utils.tests import catch_signal
1414

15-
from ...tests.utils import TransactionTestMixin
1615
from .. import settings as app_settings
1716
from ..signals import config_modified, config_status_changed
1817
from ..tasks import logger as task_logger
@@ -356,7 +355,10 @@ def test_context_regression(self):
356355
template_qs = Template.objects.filter(type='vpn')
357356
self.assertEqual(template_qs.count(), 1)
358357
t = template_qs.first()
359-
self.assertDictContainsSubset(_original_context, t.get_context())
358+
context = t.get_context()
359+
# check all items from original context exist in template context
360+
for key, value in _original_context.items():
361+
self.assertEqual(context.get(key), value)
360362
self.assertEqual(app_settings.CONTEXT, _original_context)
361363

362364
with self.subTest(
@@ -517,7 +519,6 @@ def test_regression_preventing_from_fixing_invalid_conf(self):
517519

518520

519521
class TestTemplateTransaction(
520-
TransactionTestMixin,
521522
CreateConfigTemplateMixin,
522523
TestVpnX509Mixin,
523524
TransactionTestCase,
@@ -554,7 +555,7 @@ def test_config_status_modified_after_change(self):
554555
with catch_signal(config_status_changed) as handler:
555556
t.config['interfaces'][0]['name'] = 'eth2'
556557
t.full_clean()
557-
with self.assertNumQueries(9):
558+
with self.assertNumQueries(10):
558559
t.save()
559560
c.refresh_from_db()
560561
handler.assert_not_called()

openwisp_controller/config/validators.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.utils.translation import gettext_lazy as _
33

44
key_validator = RegexValidator(
5-
_lazy_re_compile('^[^\s/\.]+$'),
5+
_lazy_re_compile(r'^[^\s/\.]+$'),
66
message=_('Key must not contain spaces, dots or slashes.'),
77
code='invalid',
88
)
@@ -16,9 +16,9 @@
1616

1717
# device name must either be a hostname or a valid mac address
1818
hostname_regex = (
19-
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}'
20-
'[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9]'
21-
'[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$'
19+
r'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}'
20+
r'[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9]'
21+
r'[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$'
2222
)
2323
device_name_validator = RegexValidator(
2424
_lazy_re_compile('{0}|{1}'.format(hostname_regex, mac_address_regex)),

0 commit comments

Comments
 (0)