openwisp-controller 1.1.1__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/CHANGES.rst +36 -1
  2. {openwisp_controller-1.1.1/openwisp_controller.egg-info → openwisp_controller-1.1.2}/PKG-INFO +7 -6
  3. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/README.rst +4 -4
  4. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/__init__.py +1 -1
  5. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/admin.py +2 -1
  6. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/apps.py +27 -1
  7. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/base.py +79 -1
  8. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/config.py +103 -50
  9. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/template.py +22 -21
  10. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/vpn.py +9 -7
  11. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/controller/views.py +37 -6
  12. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/handlers.py +2 -2
  13. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tasks.py +31 -2
  14. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_admin.py +265 -8
  15. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_api.py +70 -0
  16. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_config.py +65 -5
  17. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_controller.py +92 -12
  18. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_handlers.py +1 -1
  19. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_vpn.py +55 -14
  20. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/utils.py +1 -1
  21. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/utils.py +2 -1
  22. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/validators.py +4 -4
  23. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/admin.py +9 -1
  24. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/api/serializer.py +13 -0
  25. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/base/models.py +30 -8
  26. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/channels/consumers.py +4 -2
  27. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/commands.py +1 -1
  28. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/openwrt/ssh.py +5 -2
  29. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/pytest.py +42 -9
  30. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_api.py +38 -2
  31. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_models.py +18 -3
  32. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/api/views.py +2 -3
  33. openwisp_controller-1.1.2/openwisp_controller/geo/static/leaflet/draw/leaflet.draw.i18n.js +116 -0
  34. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/test_api.py +47 -17
  35. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/utils.py +7 -0
  36. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/utils.py +5 -5
  37. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/mixins.py +2 -2
  38. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2/openwisp_controller.egg-info}/PKG-INFO +7 -6
  39. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller.egg-info/SOURCES.txt +1 -0
  40. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/LICENSE +0 -0
  41. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/MANIFEST.in +0 -0
  42. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/admin.py +0 -0
  43. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/base.py +0 -0
  44. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/checks.py +0 -0
  45. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/__init__.py +0 -0
  46. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/__init__.py +0 -0
  47. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/download_views.py +0 -0
  48. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/filters.py +0 -0
  49. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/serializers.py +0 -0
  50. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/urls.py +0 -0
  51. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/views.py +0 -0
  52. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/api/zerotier_service.py +0 -0
  53. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/__init__.py +0 -0
  54. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/channels_consumer.py +0 -0
  55. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/device.py +0 -0
  56. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/device_group.py +0 -0
  57. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/multitenancy.py +0 -0
  58. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/base/tag.py +0 -0
  59. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/controller/__init__.py +0 -0
  60. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/controller/urls.py +0 -0
  61. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/crypto.py +0 -0
  62. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/exceptions.py +0 -0
  63. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/exportable.py +0 -0
  64. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/filters.py +0 -0
  65. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/fixtures/test_templates.json +0 -0
  66. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py +0 -0
  67. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0003_template_tags.py +0 -0
  68. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0004_add_device_model.py +0 -0
  69. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0005_populate_device.py +0 -0
  70. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0006_config_device_not_null.py +0 -0
  71. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0007_simplify_config.py +0 -0
  72. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0008_update_indexes.py +0 -0
  73. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0009_device_system.py +0 -0
  74. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0010_auto_20180106_1814.py +0 -0
  75. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0011_update_device_mac_address.py +0 -0
  76. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0012_auto_20180219_1501.py +0 -0
  77. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0013_last_ip_management_ip_and_status_applied.py +0 -0
  78. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0014_device_hardware_id.py +0 -0
  79. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0015_default_groups_permissions.py +0 -0
  80. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0016_default_organization_config_settings.py +0 -0
  81. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0017_template_name_organization_unique_together.py +0 -0
  82. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0018_config_context.py +0 -0
  83. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0019_organization_mac_add_hardware_id_name_unique_together.py +0 -0
  84. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0020_remove_config_organization.py +0 -0
  85. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0021_vpn_key.py +0 -0
  86. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0022_vpn_format_dh.py +0 -0
  87. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0023_update_context.py +0 -0
  88. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0024_update_context_data.py +0 -0
  89. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0025_update_device_key.py +0 -0
  90. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0026_hardware_id_not_unique.py +0 -0
  91. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0027_add_indexes_on_ip_fields.py +0 -0
  92. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0028_template_default_values.py +0 -0
  93. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0029_merge_django_netjsonconfig.py +0 -0
  94. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0030_django_taggit_update.py +0 -0
  95. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0031_update_vpn_dh_param.py +0 -0
  96. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0032_update_legacy_vpn_backend.py +0 -0
  97. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0033_name_unique_per_organization.py +0 -0
  98. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0034_template_required.py +0 -0
  99. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0035_device_name_unique_optional.py +0 -0
  100. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0036_device_group.py +0 -0
  101. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0037_alter_taggedtemplate.py +0 -0
  102. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0038_vpn_subnet.py +0 -0
  103. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0039_wireguard_vxlan_ipam.py +0 -0
  104. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0040_vpnclient_ip_setnull.py +0 -0
  105. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0041_default_groups_organizationconfigsettings_permission.py +0 -0
  106. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0042_multiple_wireguard_tunnels.py +0 -0
  107. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0043_devicegroup_templates.py +0 -0
  108. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0044_config_error_reason.py +0 -0
  109. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0045_alter_vpn_webhook_endpoint.py +0 -0
  110. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0046_organizationlimits.py +0 -0
  111. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0047_add_organizationlimits.py +0 -0
  112. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0048_wifi_radio_band_migration.py +0 -0
  113. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0049_devicegroup_context.py +0 -0
  114. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0050_alter_vpnclient_unique_together.py +0 -0
  115. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0051_organizationconfigsettings_context.py +0 -0
  116. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0052_vpn_node_network_id.py +0 -0
  117. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0053_vpnclient_secret.py +0 -0
  118. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0054_device__is_deactivated.py +0 -0
  119. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/0055_alter_config_status.py +0 -0
  120. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/migrations/__init__.py +0 -0
  121. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/models.py +0 -0
  122. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/settings.py +0 -0
  123. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/signals.py +0 -0
  124. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/sortedm2m/__init__.py +0 -0
  125. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/sortedm2m/fields.py +0 -0
  126. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/sortedm2m/forms.py +0 -0
  127. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/css/admin.css +0 -0
  128. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/css/device-delete-confirmation.css +0 -0
  129. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/css/devicegroup.css +0 -0
  130. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/css/lib/advanced-mode.css +0 -0
  131. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/css/lib/img/jsoneditor-icons.svg +0 -0
  132. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/css/lib/jsonschema-ui.css +0 -0
  133. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/device-delete-confirmation.js +0 -0
  134. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/lib/advanced-mode.js +0 -0
  135. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/lib/jsonschema-ui.js +0 -0
  136. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/lib/tomorrow_night_bright.js +0 -0
  137. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/management_ip.js +0 -0
  138. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/preview.js +0 -0
  139. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/relevant_templates.js +0 -0
  140. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/switcher.js +0 -0
  141. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/tabs.js +0 -0
  142. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/unsaved_changes.js +0 -0
  143. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/utils.js +0 -0
  144. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/vpn.js +0 -0
  145. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/config/js/widget.js +0 -0
  146. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/import_export/import-openwisp.css +0 -0
  147. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/sortedm2m/patch_sortedm2m.js +0 -0
  148. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/static/support.css +0 -0
  149. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tasks_zerotier.py +0 -0
  150. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/change_device_group.html +0 -0
  151. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/change_form.html +0 -0
  152. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/change_list_device.html +0 -0
  153. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/clone_template_form.html +0 -0
  154. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/device/change_form.html +0 -0
  155. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/device/delete_confirmation.html +0 -0
  156. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +0 -0
  157. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/device_recover_form.html +0 -0
  158. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/jsonschema-widget.html +0 -0
  159. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/preview.html +0 -0
  160. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/config/system_context.html +0 -0
  161. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/device_group/change_form.html +0 -0
  162. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/admin/import_export/import.html +0 -0
  163. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/templates/reversion/config/revision_form.html +0 -0
  164. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/__init__.py +0 -0
  165. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/pytest.py +0 -0
  166. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_apps.py +0 -0
  167. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_device.py +0 -0
  168. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_device_group.py +0 -0
  169. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_notifications.py +0 -0
  170. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_selenium.py +0 -0
  171. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_tag.py +0 -0
  172. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_template.py +0 -0
  173. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/tests/test_views.py +0 -0
  174. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/urls.py +0 -0
  175. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/views.py +0 -0
  176. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/config/widgets.py +0 -0
  177. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/__init__.py +0 -0
  178. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/api/urls.py +0 -0
  179. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/api/views.py +0 -0
  180. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/apps.py +0 -0
  181. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/base/__init__.py +0 -0
  182. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/channels/routing.py +0 -0
  183. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/__init__.py +0 -0
  184. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/airos/snmp.py +0 -0
  185. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/exceptions.py +0 -0
  186. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/openwrt/__init__.py +0 -0
  187. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/openwrt/snmp.py +0 -0
  188. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/snmp.py +0 -0
  189. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/connectors/ssh.py +0 -0
  190. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/exceptions.py +0 -0
  191. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0001_initial.py +0 -0
  192. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0002_credentials_auto_add.py +0 -0
  193. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0003_default_group_permissions.py +0 -0
  194. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0004_django3_1_upgrade.py +0 -0
  195. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0005_device_connection_failure_reason.py +0 -0
  196. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0006_name_unique_per_organization.py +0 -0
  197. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0007_command.py +0 -0
  198. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0008_remove_conflicting_deviceconnections.py +0 -0
  199. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/0009_alter_deviceconnection_unique_together.py +0 -0
  200. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/migrations/__init__.py +0 -0
  201. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/models.py +0 -0
  202. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/schema.py +0 -0
  203. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/settings.py +0 -0
  204. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/signals.py +0 -0
  205. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/static/connection/css/command-inline.css +0 -0
  206. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/static/connection/css/credentials.css +0 -0
  207. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/static/connection/js/commands.js +0 -0
  208. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/static/connection/js/credentials.js +0 -0
  209. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/static/connection/js/lib/reconnecting-websocket.min.js +0 -0
  210. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tasks.py +0 -0
  211. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/__init__.py +0 -0
  212. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/base.py +0 -0
  213. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test-key.ed25519 +0 -0
  214. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test-key.rsa +0 -0
  215. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_admin.py +0 -0
  216. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_command_utilities.py +0 -0
  217. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_notifications.py +0 -0
  218. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_selenium.py +0 -0
  219. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_snmp.py +0 -0
  220. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_ssh.py +0 -0
  221. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/test_tasks.py +0 -0
  222. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/tests/utils.py +0 -0
  223. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/utils.py +0 -0
  224. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/connection/widgets.py +0 -0
  225. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/context_processors.py +0 -0
  226. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/__init__.py +0 -0
  227. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/admin.py +0 -0
  228. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/api/__init__.py +0 -0
  229. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/api/filters.py +0 -0
  230. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/api/serializers.py +0 -0
  231. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/api/urls.py +0 -0
  232. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/apps.py +0 -0
  233. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/base/__init__.py +0 -0
  234. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/base/models.py +0 -0
  235. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/channels/__init__.py +0 -0
  236. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/channels/consumers.py +0 -0
  237. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/channels/routing.py +0 -0
  238. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/exportable.py +0 -0
  239. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/migrations/0001_initial.py +0 -0
  240. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/migrations/0002_default_groups_permissions.py +0 -0
  241. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/migrations/0003_alter_devicelocation_floorplan_location.py +0 -0
  242. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/migrations/__init__.py +0 -0
  243. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/models.py +0 -0
  244. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/templates/admin/widgets/foreign_key_raw_id.html +0 -0
  245. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/__init__.py +0 -0
  246. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/pytest.py +0 -0
  247. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/test_admin.py +0 -0
  248. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/test_admin_inline.py +0 -0
  249. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/test_apps.py +0 -0
  250. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/geo/tests/test_models.py +0 -0
  251. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/migrations.py +0 -0
  252. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/__init__.py +0 -0
  253. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/admin.py +0 -0
  254. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/api/serializers.py +0 -0
  255. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/api/urls.py +0 -0
  256. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/api/views.py +0 -0
  257. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/apps.py +0 -0
  258. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/base/__init__.py +0 -0
  259. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/base/models.py +0 -0
  260. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0001_initial.py +0 -0
  261. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0002_add_organization_name.py +0 -0
  262. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0003_fill_organization_name.py +0 -0
  263. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0004_auto_20180106_1814.py +0 -0
  264. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0005_organizational_unit_name.py +0 -0
  265. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0006_add_x509_passphrase_field.py +0 -0
  266. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0007_default_groups_permissions.py +0 -0
  267. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0008_serial_number_length.py +0 -0
  268. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0009_common_name_maxlength_64.py +0 -0
  269. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0010_common_name_organization_unique.py +0 -0
  270. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/0011_disallowed_blank_key_length_or_digest.py +0 -0
  271. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/migrations/__init__.py +0 -0
  272. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/models.py +0 -0
  273. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/static/admin/pki/js/show-org-field.js +0 -0
  274. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/templates/admin/pki/change_form.html +0 -0
  275. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/tests/__init__.py +0 -0
  276. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/tests/test_admin.py +0 -0
  277. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/tests/test_api.py +0 -0
  278. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/tests/test_models.py +0 -0
  279. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/tests/utils.py +0 -0
  280. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/urls.py +0 -0
  281. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/pki/utils.py +0 -0
  282. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/routing.py +0 -0
  283. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/settings.py +0 -0
  284. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/__init__.py +0 -0
  285. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/admin.py +0 -0
  286. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/apps.py +0 -0
  287. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/base/models.py +0 -0
  288. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/filters.py +0 -0
  289. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/migrations/0001_initial.py +0 -0
  290. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/migrations/0002_default_group_migration.py +0 -0
  291. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/migrations/0003_related_field_allow_blank.py +0 -0
  292. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/migrations/0004_index_rule_on_delete.py +0 -0
  293. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/migrations/0005_number_of_subnets_and_ips.py +0 -0
  294. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/migrations/__init__.py +0 -0
  295. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/models.py +0 -0
  296. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/rule_types/__init__.py +0 -0
  297. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/rule_types/base.py +0 -0
  298. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/rule_types/device.py +0 -0
  299. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/rule_types/vpn.py +0 -0
  300. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/settings.py +0 -0
  301. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/signals.py +0 -0
  302. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/static/subnet-division/css/subnet-division.css +0 -0
  303. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/static/subnet-division/js/subnet-division.js +0 -0
  304. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/tasks.py +0 -0
  305. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/tests/__init__.py +0 -0
  306. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/tests/helpers.py +0 -0
  307. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/tests/test_admin.py +0 -0
  308. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/tests/test_models.py +0 -0
  309. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/tests/test_rule.py +0 -0
  310. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/subnet_division/utils.py +0 -0
  311. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/tests/__init__.py +0 -0
  312. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/tests/mixins.py +0 -0
  313. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/tests/test_selenium.py +0 -0
  314. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/tests/test_users_integration.py +0 -0
  315. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/tests/test_utilities.py +0 -0
  316. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/tests/utils.py +0 -0
  317. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/urls.py +0 -0
  318. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller/vpn_backends.py +0 -0
  319. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller.egg-info/dependency_links.txt +0 -0
  320. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller.egg-info/not-zip-safe +0 -0
  321. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller.egg-info/requires.txt +0 -0
  322. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/openwisp_controller.egg-info/top_level.txt +0 -0
  323. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/pyproject.toml +0 -0
  324. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/requirements.txt +0 -0
  325. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/setup.cfg +0 -0
  326. {openwisp_controller-1.1.1 → openwisp_controller-1.1.2}/setup.py +0 -0
@@ -1,6 +1,41 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ Version 1.1.2 [2025-08-01]
5
+ --------------------------
6
+
7
+ Bugfixes
8
+ ~~~~~~~~
9
+
10
+ - Fixed `compatibility of OpenWrt.update_config with openwisp-config >=
11
+ 1.1.0 <https://github.com/openwisp/openwisp-controller/issues/964>`__.
12
+ - Fixed `VPN-client template switch issue when switching between VPN
13
+ servers with the same address
14
+ <https://github.com/openwisp/openwisp-controller/issues/973>`__.
15
+ - Fixed `missing config_changed signal when reverting a template
16
+ <https://github.com/openwisp/openwisp-controller/issues/836>`__.
17
+ - Fixed `Zerotier network name being set to "ow_zt" instead of "global"
18
+ <https://github.com/openwisp/openwisp-controller/issues/982>`__.
19
+ - Fixed error in preview when device has no name set.
20
+ - Fixed live updates for "Send commands" when multiple websocket
21
+ connections are open for the same device.
22
+ - Limited command results in the device admin to 30 entries to avoid
23
+ loading issues.
24
+ - Fixed uncaught exception in ``leaflet.draw.i18n.js``.
25
+ - Fixed `handling of devices without a DeviceConnection when creating
26
+ commands
27
+ <https://github.com/openwisp/openwisp-controller/issues/1016>`__.
28
+ - Prevented `adding multiple VPN client templates pointing to the same VPN
29
+ server <https://github.com/openwisp/openwisp-controller/issues/832>`__.
30
+ - Fixed timeout issues in VPN checksum and download configuration views
31
+ for large VPN setups (e.g., 2000+ WireGuard peers) by caching the
32
+ checksum and generated configuration.
33
+ - Fixed an issue where enforcing required templates was accidentally
34
+ deleting `VpnClient` objects, causing them to be recreated whenever
35
+ device templates were modified in the Django admin.
36
+ - Fixed ``HTTP 500 Server Error`` when geographic REST API endpoints
37
+ receive a malformed resource ID.
38
+
4
39
  Version 1.1.1 [2025-01-31]
5
40
  --------------------------
6
41
 
@@ -9,7 +44,7 @@ Bugfixes
9
44
 
10
45
  - Fixed `recovering deleted device with related location
11
46
  <https://github.com/openwisp/openwisp-controller/issues/936>`__.
12
- - Fixed `deleting device with “deactivating” config status
47
+ - Fixed `deleting device with "deactivating" config status
13
48
  <https://github.com/openwisp/openwisp-controller/issues/949>`__.
14
49
  - Fixed `conversion of MAC address to uppercase format
15
50
  <https://github.com/openwisp/openwisp-controller/issues/922>`__ with the
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: openwisp-controller
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: OpenWISP Controller
5
5
  Home-page: http://openwisp.org
6
6
  Download-URL: https://github.com/openwisp/openwisp-controller/releases
@@ -45,6 +45,7 @@ Dynamic: download-url
45
45
  Dynamic: home-page
46
46
  Dynamic: keywords
47
47
  Dynamic: license
48
+ Dynamic: license-file
48
49
  Dynamic: platform
49
50
  Dynamic: requires-dist
50
51
  Dynamic: summary
@@ -106,10 +107,10 @@ Other popular building blocks that are part of the OpenWISP ecosystem are:
106
107
  - `openwisp-firmware-upgrader
107
108
  <https://openwisp.io/docs/stable/firmware-upgrader/>`_: automated
108
109
  firmware upgrades (single devices or mass network upgrades)
109
- - `openwisp-radius <https://openwisp.io/docs/stable/user/radius.html>`_:
110
- based on FreeRADIUS, allows to implement network access authentication
111
- systems like 802.1x WPA2 Enterprise, captive portal authentication,
112
- Hotspot 2.0 (802.11u)
110
+ - `openwisp-radius <https://openwisp.io/docs/stable/radius/>`_: based on
111
+ FreeRADIUS, allows to implement network access authentication systems
112
+ like 802.1x WPA2 Enterprise, captive portal authentication, Hotspot 2.0
113
+ (802.11u)
113
114
  - `openwisp-network-topology
114
115
  <https://openwisp.io/docs/stable/network-topology/>`_: provides way to
115
116
  collect and visualize network topology data from dynamic mesh routing
@@ -55,10 +55,10 @@ Other popular building blocks that are part of the OpenWISP ecosystem are:
55
55
  - `openwisp-firmware-upgrader
56
56
  <https://openwisp.io/docs/stable/firmware-upgrader/>`_: automated
57
57
  firmware upgrades (single devices or mass network upgrades)
58
- - `openwisp-radius <https://openwisp.io/docs/stable/user/radius.html>`_:
59
- based on FreeRADIUS, allows to implement network access authentication
60
- systems like 802.1x WPA2 Enterprise, captive portal authentication,
61
- Hotspot 2.0 (802.11u)
58
+ - `openwisp-radius <https://openwisp.io/docs/stable/radius/>`_: based on
59
+ FreeRADIUS, allows to implement network access authentication systems
60
+ like 802.1x WPA2 Enterprise, captive portal authentication, Hotspot 2.0
61
+ (802.11u)
62
62
  - `openwisp-network-topology
63
63
  <https://openwisp.io/docs/stable/network-topology/>`_: provides way to
64
64
  collect and visualize network topology data from dynamic mesh routing
@@ -1,4 +1,4 @@
1
- VERSION = (1, 1, 1, 'final')
1
+ VERSION = (1, 1, 2, 'final')
2
2
  __version__ = VERSION # alias
3
3
 
4
4
 
@@ -311,7 +311,8 @@ class BaseConfigAdmin(BaseAdmin):
311
311
  config = instance.config
312
312
  else:
313
313
  raise Http404()
314
- config_archive = config.generate()
314
+ get_configuration = getattr(config, "get_cached_configuration", config.generate)
315
+ config_archive = get_configuration()
315
316
  return send_file(
316
317
  filename='{0}.tar.gz'.format(config.name),
317
318
  contents=config_archive.getvalue(),
@@ -55,6 +55,7 @@ class ConfigConfig(AppConfig):
55
55
 
56
56
  def __setmodels__(self):
57
57
  self.device_model = load_model('config', 'Device')
58
+ self.template_model = load_model('config', 'Template')
58
59
  self.devicegroup_model = load_model('config', 'DeviceGroup')
59
60
  self.config_model = load_model('config', 'Config')
60
61
  self.vpn_model = load_model('config', 'Vpn')
@@ -135,11 +136,21 @@ class ConfigConfig(AppConfig):
135
136
  sender=self.config_model,
136
137
  dispatch_uid='devicegroup_templates_change_handler.backend_changed',
137
138
  )
139
+ pre_save.connect(
140
+ self.template_model.pre_save_handler,
141
+ sender=self.template_model,
142
+ dispatch_uid='template_pre_save_handler',
143
+ )
138
144
  pre_save.connect(
139
145
  handlers.organization_disabled_handler,
140
146
  sender=self.org_model,
141
147
  dispatch_uid='organization_disabled_pre_save_clear_device_checksum_cache',
142
148
  )
149
+ post_save.connect(
150
+ self.template_model.post_save_handler,
151
+ sender=self.template_model,
152
+ dispatch_uid='template_post_save_handler',
153
+ )
143
154
  post_save.connect(
144
155
  self.org_limits.post_save_handler,
145
156
  sender=self.org_model,
@@ -294,7 +305,7 @@ class ConfigConfig(AppConfig):
294
305
  Triggers the cache invalidation for the
295
306
  device config checksum (view and model method)
296
307
  """
297
- from .controller.views import DeviceChecksumView
308
+ from .controller.views import DeviceChecksumView, GetVpnView
298
309
  from .handlers import (
299
310
  device_cache_invalidation_handler,
300
311
  devicegroup_change_handler,
@@ -323,6 +334,21 @@ class ConfigConfig(AppConfig):
323
334
  DeviceChecksumView.invalidate_checksum_cache,
324
335
  dispatch_uid='invalidate_checksum_cache',
325
336
  )
337
+ # VPN cache invalidation
338
+ post_save.connect(
339
+ GetVpnView.invalidate_get_vpn_cache,
340
+ sender=self.vpn_model,
341
+ dispatch_uid="invalidate_get_vpn_cache",
342
+ )
343
+ pre_delete.connect(
344
+ GetVpnView.invalidate_get_vpn_cache,
345
+ sender=self.vpn_model,
346
+ dispatch_uid="vpn_server_pre_delete_invalidate_get_vpn_cache",
347
+ )
348
+ vpn_server_modified.connect(
349
+ GetVpnView.invalidate_get_vpn_cache,
350
+ dispatch_uid="vpn_server_modified_invalidate_get_vpn_cache",
351
+ )
326
352
  device_group_changed.connect(
327
353
  devicegroup_change_handler,
328
354
  sender=self.device_model,
@@ -1,8 +1,10 @@
1
1
  import collections
2
2
  import hashlib
3
3
  import json
4
+ import logging
4
5
  from copy import deepcopy
5
6
 
7
+ from cache_memoize import cache_memoize
6
8
  from django.core.exceptions import ValidationError
7
9
  from django.db import models
8
10
  from django.utils.functional import cached_property
@@ -15,6 +17,82 @@ from openwisp_utils.base import TimeStampedEditableModel
15
17
 
16
18
  from .. import settings as app_settings
17
19
 
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def get_cached_args_rewrite(instance):
24
+ """
25
+ Use only the PK parameter for calculating the cache key
26
+ """
27
+ return instance.pk.hex
28
+
29
+
30
+ class ChecksumCacheMixin:
31
+ """
32
+ Mixin that provides caching for checksum.
33
+ """
34
+
35
+ _CHECKSUM_CACHE_TIMEOUT = 60 * 60 * 24 * 30 # 30 days
36
+
37
+ @cache_memoize(
38
+ timeout=_CHECKSUM_CACHE_TIMEOUT, args_rewrite=get_cached_args_rewrite
39
+ )
40
+ def get_cached_checksum(self):
41
+ """
42
+ Handles caching,
43
+ timeout=None means value is cached indefinitely
44
+ (invalidation handled on post_save/post_delete signal)
45
+ """
46
+ logger.debug(f"calculating checksum for {self.__class__.__name__} ID {self.pk}")
47
+ return self.checksum
48
+
49
+ @classmethod
50
+ def bulk_invalidate_get_cached_checksum(cls, query_params):
51
+ """
52
+ Bulk invalidate checksum cache for multiple instances
53
+ """
54
+ for instance in cls.objects.only("id").filter(**query_params).iterator():
55
+ instance.get_cached_checksum.invalidate(instance)
56
+
57
+ def invalidate_checksum_cache(self):
58
+ """
59
+ Invalidate the checksum cache for this instance
60
+ """
61
+ self.get_cached_checksum.invalidate(self)
62
+ logger.debug(
63
+ f"invalidated checksum cache for {self.__class__.__name__} ID {self.pk}"
64
+ )
65
+
66
+
67
+ class ConfigChecksumCacheMixin(ChecksumCacheMixin):
68
+ """
69
+ Mixin that provides caching for both checksum and configuration.
70
+ """
71
+
72
+ @cache_memoize(
73
+ timeout=ChecksumCacheMixin._CHECKSUM_CACHE_TIMEOUT,
74
+ args_rewrite=get_cached_args_rewrite,
75
+ )
76
+ def get_cached_configuration(self):
77
+ """
78
+ Returns cached configuration
79
+ """
80
+ return self.generate()
81
+
82
+ def invalidate_configuration_cache(self):
83
+ """
84
+ Invalidate the configuration cache for this instance
85
+ """
86
+ self.get_cached_configuration.invalidate(self)
87
+ logger.debug(
88
+ f"invalidated configuration cache for {self.__class__.__name__}"
89
+ f" ID {self.pk}"
90
+ )
91
+
92
+ def invalidate_checksum_cache(self):
93
+ super().invalidate_checksum_cache()
94
+ self.invalidate_configuration_cache()
95
+
18
96
 
19
97
  class BaseModel(TimeStampedEditableModel):
20
98
  """
@@ -89,7 +167,7 @@ class BaseConfig(BaseModel):
89
167
  return config
90
168
  c = deepcopy(config)
91
169
  is_config = not any([self.__template__, self.__vpn__])
92
- if 'hostname' not in c.get('general', {}) and is_config:
170
+ if all(('hostname' not in c.get('general', {}), is_config, self.name)):
93
171
  c.setdefault('general', {})
94
172
  c['general']['hostname'] = self.name.replace(':', '-')
95
173
  return c
@@ -1,8 +1,8 @@
1
1
  import collections
2
2
  import logging
3
3
  import re
4
+ from collections import defaultdict
4
5
 
5
- from cache_memoize import cache_memoize
6
6
  from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
7
7
  from django.db import models, transaction
8
8
  from django.utils.translation import gettext_lazy as _
@@ -23,11 +23,20 @@ from ..signals import (
23
23
  )
24
24
  from ..sortedm2m.fields import SortedManyToManyField
25
25
  from ..utils import get_default_templates_queryset
26
- from .base import BaseConfig
26
+ from .base import BaseConfig, ChecksumCacheMixin, get_cached_args_rewrite
27
27
 
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
30
 
31
+ def get_cached_checksum_args_rewrite(config):
32
+ """
33
+ DEPRECATED: Use get_cached_args_rewrite instead.
34
+
35
+ TODO: Remove this in 1.2.0 release.
36
+ """
37
+ return get_cached_args_rewrite(config)
38
+
39
+
31
40
  class TemplatesThrough(object):
32
41
  """
33
42
  Improves string representation of m2m relationship objects
@@ -37,14 +46,7 @@ class TemplatesThrough(object):
37
46
  return _('Relationship with {0}').format(self.template.name)
38
47
 
39
48
 
40
- def get_cached_checksum_args_rewrite(config):
41
- """
42
- Use only the PK parameter for calculating the cache key
43
- """
44
- return config.pk.hex
45
-
46
-
47
- class AbstractConfig(BaseConfig):
49
+ class AbstractConfig(ChecksumCacheMixin, BaseConfig):
48
50
  """
49
51
  Abstract model implementing the
50
52
  NetJSON DeviceConfiguration object
@@ -149,23 +151,6 @@ class AbstractConfig(BaseConfig):
149
151
  """
150
152
  return self.device.key
151
153
 
152
- @cache_memoize(
153
- timeout=_CHECKSUM_CACHE_TIMEOUT, args_rewrite=get_cached_checksum_args_rewrite
154
- )
155
- def get_cached_checksum(self):
156
- """
157
- Handles caching,
158
- timeout=None means value is cached indefinitely
159
- (invalidation handled on post_save/post_delete signal)
160
- """
161
- logger.debug(f'calculating checksum for config ID {self.pk}')
162
- return self.checksum
163
-
164
- @classmethod
165
- def bulk_invalidate_get_cached_checksum(cls, query_params):
166
- for config in cls.objects.only('id').filter(**query_params).iterator():
167
- config.get_cached_checksum.invalidate(config)
168
-
169
154
  @classmethod
170
155
  def get_template_model(cls):
171
156
  return cls.templates.rel.model
@@ -185,6 +170,60 @@ class AbstractConfig(BaseConfig):
185
170
  templates = pk_set
186
171
  return templates
187
172
 
173
+ @classmethod
174
+ def clean_duplicate_vpn_client_templates(
175
+ cls, action, instance, templates, raw_data=None
176
+ ):
177
+ """
178
+ Multiple VPN client templates related to the same VPN server are not allowed:
179
+ it hardly makes sense, preventing it keeps things simple and avoids headaches.
180
+ Raises a ValidationError if duplicates are found.
181
+ """
182
+ if action != "pre_add":
183
+ return
184
+
185
+ def format_template_list(names):
186
+ quoted = [f'"{name}"' for name in names]
187
+ if len(quoted) == 2:
188
+ return " and ".join(quoted)
189
+ return ", ".join(quoted[:-1]) + " and " + quoted[-1]
190
+
191
+ def add_vpn_templates(templates_queryset):
192
+ for template in templates_queryset.filter(type="vpn"):
193
+ if template.name not in vpn_templates[template.vpn.name]:
194
+ vpn_templates[template.vpn.name].append(template.name)
195
+
196
+ raw_data = raw_data or {}
197
+ vpn_templates = defaultdict(list)
198
+ if not raw_data:
199
+ # When raw_data is present, validation is triggered by a
200
+ # ConfigForm submission.
201
+ # In this case, the "templates" queryset already contains only the templates
202
+ # that are intended to be assigned. Templates that would be removed
203
+ # (e.g., in a pre_clear action) have already been excluded from the
204
+ # queryset.
205
+ add_vpn_templates(instance.templates)
206
+ add_vpn_templates(templates)
207
+
208
+ error_lines = [
209
+ _(
210
+ "You cannot select multiple VPN client templates related to"
211
+ " the same VPN server."
212
+ )
213
+ ]
214
+ for vpn_name, template_names in vpn_templates.items():
215
+ if len(template_names) < 2:
216
+ continue
217
+ template_list = format_template_list(sorted(template_names))
218
+ error_lines.append(
219
+ _(
220
+ "The templates {template_list} are all linked"
221
+ ' to the same VPN server: "{vpn_name}".'
222
+ ).format(template_list=template_list, vpn_name=vpn_name)
223
+ )
224
+ if len(error_lines) > 1:
225
+ raise ValidationError("\n".join(str(line) for line in error_lines))
226
+
188
227
  @classmethod
189
228
  def clean_templates(cls, action, instance, pk_set, raw_data=None, **kwargs):
190
229
  """
@@ -203,6 +242,9 @@ class AbstractConfig(BaseConfig):
203
242
  )
204
243
  if not templates:
205
244
  return
245
+ cls.clean_duplicate_vpn_client_templates(
246
+ action, instance, templates, raw_data=raw_data
247
+ )
206
248
  backend = instance.get_backend_instance(template_instances=templates)
207
249
  try:
208
250
  cls.clean_netjsonconfig_backend(backend)
@@ -256,7 +298,7 @@ class AbstractConfig(BaseConfig):
256
298
 
257
299
  if action == 'post_clear':
258
300
  if instance.is_deactivating_or_deactivated():
259
- # If the device is deactivated or in the process of deactivatiing, then
301
+ # If the device is deactivated or in the process of deactivating, then
260
302
  # delete all vpn clients and return.
261
303
  instance.vpnclient_set.all().delete()
262
304
  return
@@ -265,37 +307,48 @@ class AbstractConfig(BaseConfig):
265
307
  # coming from signal
266
308
  if isinstance(pk_set, set):
267
309
  template_model = cls.get_template_model()
268
- # Ordering the queryset here doesn't affect the functionality
269
- # since pk_set is a set. Though ordering the queryset is required
270
- # for tests.
271
310
  templates = template_model.objects.filter(pk__in=list(pk_set)).order_by(
272
311
  'created'
273
312
  )
274
313
  # coming from admin ModelForm
275
314
  else:
276
315
  templates = pk_set
277
- # delete VPN clients which have been cleared
278
- # by sortedm2m and have not been added back
279
- if action == 'post_add':
280
- vpn_list = instance.templates.filter(type='vpn').values_list('vpn')
281
- instance.vpnclient_set.exclude(vpn__in=vpn_list).delete()
282
- # when adding or removing specific templates
316
+
317
+ # Handle template actions
283
318
  for template in templates.filter(type='vpn'):
284
319
  if action == 'post_add':
285
- if vpn_client_model.objects.filter(
320
+ # Create VPN client if needed
321
+ if not vpn_client_model.objects.filter(
286
322
  config=instance, vpn=template.vpn
287
323
  ).exists():
288
- continue
289
- client = vpn_client_model(
290
- config=instance,
291
- vpn=template.vpn,
292
- auto_cert=template.auto_cert,
293
- )
294
- client.full_clean()
295
- client.save()
296
- elif action == 'post_remove':
297
- for client in instance.vpnclient_set.filter(vpn=template.vpn):
298
- client.delete()
324
+ client = vpn_client_model(
325
+ config=instance,
326
+ vpn=template.vpn,
327
+ auto_cert=template.auto_cert,
328
+ )
329
+ client.full_clean()
330
+ client.save()
331
+
332
+ if len(pk_set) != templates.filter(required=True).count():
333
+ # Explanation:
334
+ # SortedManyToManyField clears all existing templates before adding
335
+ # new ones. This triggers an m2m_changed signal with the "post_clear"
336
+ # action, which is handled by the "enforce_required_templates" signal
337
+ # receiver. That receiver re-adds the required templates.
338
+ #
339
+ # Re-adding required templates triggers another m2m_changed signal
340
+ # with the "post_add" action. At this stage, only required templates
341
+ # exist in the DB, so we cannot yet determine which VpnClient objects
342
+ # should be deleted based on the new selection.
343
+ #
344
+ # Therefore, we defer deletion of VpnClient objects until the "post_add"
345
+ # signal is triggered again—after all templates, including the required
346
+ # ones, have been fully added. At that point, we can identify and
347
+ # delete VpnClient objects not linked to the final template set.
348
+ current_vpns = set(
349
+ instance.templates.filter(type='vpn').values_list('vpn_id', flat=True)
350
+ )
351
+ instance.vpnclient_set.exclude(vpn_id__in=current_vpns).delete()
299
352
 
300
353
  @classmethod
301
354
  def clean_templates_org(cls, action, instance, pk_set, raw_data=None, **kwargs):
@@ -428,7 +481,7 @@ class AbstractConfig(BaseConfig):
428
481
 
429
482
  # Check if the device is using stock OpenWrt.
430
483
  openwrt_match = re.search(
431
- '[oO][pP][eE][nN][wW][rR][tT]\s*([\d.]+)', self.device.os
484
+ r'[oO][pP][eE][nN][wW][rR][tT]\s*([\d.]+)', self.device.os
432
485
  )
433
486
  if openwrt_match:
434
487
  if version.parse(openwrt_match.group(1)) >= version.parse('21'):
@@ -106,30 +106,31 @@ class AbstractTemplate(ShareableOrgMixinUniqueName, BaseConfig):
106
106
  verbose_name_plural = _('templates')
107
107
  unique_together = (('organization', 'name'),)
108
108
 
109
- def save(self, *args, **kwargs):
109
+ @classmethod
110
+ def pre_save_handler(cls, instance, *args, **kwargs):
110
111
  """
111
- modifies status of related configs
112
- if key attributes have changed (queries the database)
112
+ Modifies status of related configs
113
113
  """
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):
131
132
  transaction.on_commit(
132
- lambda: update_template_related_config_status.delay(self.pk)
133
+ lambda: update_template_related_config_status.delay(instance.pk)
133
134
  )
134
135
 
135
136
  def _update_related_config_status(self):
@@ -34,7 +34,7 @@ from ..tasks_zerotier import (
34
34
  trigger_zerotier_server_update,
35
35
  trigger_zerotier_server_update_member,
36
36
  )
37
- from .base import BaseConfig
37
+ from .base import BaseConfig, ConfigChecksumCacheMixin
38
38
 
39
39
  logger = logging.getLogger(__name__)
40
40
 
@@ -44,7 +44,7 @@ def _peer_cache_key(vpn):
44
44
  return str(vpn.pk)
45
45
 
46
46
 
47
- class AbstractVpn(ShareableOrgMixinUniqueName, BaseConfig):
47
+ class AbstractVpn(ConfigChecksumCacheMixin, ShareableOrgMixinUniqueName, BaseConfig):
48
48
  """
49
49
  Abstract VPN model
50
50
  """
@@ -282,6 +282,7 @@ class AbstractVpn(ShareableOrgMixinUniqueName, BaseConfig):
282
282
  if create_dh:
283
283
  transaction.on_commit(lambda: create_vpn_dh.delay(self.id))
284
284
  if not created and self._send_vpn_modified_after_save:
285
+ self.invalidate_checksum_cache()
285
286
  self._send_vpn_modified_signal()
286
287
  self._send_vpn_modified_after_save = False
287
288
  # For ZeroTier VPN server, if the
@@ -309,10 +310,9 @@ class AbstractVpn(ShareableOrgMixinUniqueName, BaseConfig):
309
310
  ]
310
311
  current = self._meta.model.objects.only(*attrs).get(pk=self.pk)
311
312
  for attr in attrs:
312
- if getattr(self, attr) == getattr(current, attr):
313
- continue
314
- self._send_vpn_modified_after_save = True
315
- break
313
+ if getattr(self, attr) != getattr(current, attr):
314
+ self._send_vpn_modified_after_save = True
315
+ break
316
316
 
317
317
  def _send_vpn_modified_signal(self):
318
318
  vpn_server_modified.send(sender=self.__class__, instance=self)
@@ -634,7 +634,7 @@ class AbstractVpn(ShareableOrgMixinUniqueName, BaseConfig):
634
634
  # call auto_client and update the config
635
635
  elif self._is_backend_type('zerotier') and template_backend_class:
636
636
  auto = getattr(template_backend_class, 'zerotier_auto_client')(
637
- name='ow_zt',
637
+ name='global',
638
638
  networks=[
639
639
  {'id': self.network_id, 'ifname': f'owzt{self.network_id[-6:]}'}
640
640
  ],
@@ -688,8 +688,10 @@ class AbstractVpn(ShareableOrgMixinUniqueName, BaseConfig):
688
688
  for backend in ['wireguard', 'vxlan']:
689
689
  if self._is_backend_type(backend):
690
690
  getattr(self, f'_get_{backend}_peers').invalidate(self)
691
+ self.invalidate_checksum_cache()
691
692
  if update:
692
693
  getattr(self, f'_get_{backend}_peers')()
694
+ self.get_cached_configuration()
693
695
  # Send signal for peers changed
694
696
  vpn_peers_changed.send(sender=self.__class__, instance=self)
695
697