openwisp-controller 1.2.1__tar.gz → 1.2.3__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.
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/CHANGES.rst +24 -0
- {openwisp_controller-1.2.1/openwisp_controller.egg-info → openwisp_controller-1.2.3}/PKG-INFO +1 -1
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/__init__.py +1 -1
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/config.py +4 -4
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/device.py +3 -3
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/device_group.py +4 -4
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/multitenancy.py +2 -1
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/template.py +3 -4
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0023_update_context.py +4 -3
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0028_template_default_values.py +3 -3
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0036_device_group.py +5 -3
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0049_devicegroup_context.py +2 -2
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0051_organizationconfigsettings_context.py +2 -2
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/relevant_templates.js +1 -1
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_device.py +14 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_selenium.py +98 -2
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_vpn.py +3 -2
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tasks.py +10 -5
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_models.py +56 -12
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_tasks.py +50 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_admin.py +6 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_selenium.py +5 -1
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/base.py +28 -8
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/vpn.py +3 -2
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/test_admin.py +137 -3
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3/openwisp_controller.egg-info}/PKG-INFO +1 -1
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/LICENSE +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/MANIFEST.in +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/README.rst +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/base.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/checks.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/download_views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/filters.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/serializers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/api/zerotier_service.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/base.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/channels_consumer.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/tag.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/vpn.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/controller/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/controller/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/controller/views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/crypto.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/exceptions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/exportable.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/filters.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/fixtures/test_templates.json +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/handlers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0003_template_tags.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0004_add_device_model.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0005_populate_device.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0006_config_device_not_null.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0007_simplify_config.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0008_update_indexes.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0009_device_system.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0010_auto_20180106_1814.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0011_update_device_mac_address.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0012_auto_20180219_1501.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0013_last_ip_management_ip_and_status_applied.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0014_device_hardware_id.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0015_default_groups_permissions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0016_default_organization_config_settings.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0017_template_name_organization_unique_together.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0018_config_context.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0019_organization_mac_add_hardware_id_name_unique_together.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0020_remove_config_organization.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0021_vpn_key.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0022_vpn_format_dh.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0024_update_context_data.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0025_update_device_key.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0026_hardware_id_not_unique.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0027_add_indexes_on_ip_fields.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0029_merge_django_netjsonconfig.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0030_django_taggit_update.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0031_update_vpn_dh_param.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0032_update_legacy_vpn_backend.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0033_name_unique_per_organization.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0034_template_required.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0035_device_name_unique_optional.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0037_alter_taggedtemplate.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0038_vpn_subnet.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0039_wireguard_vxlan_ipam.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0040_vpnclient_ip_setnull.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0041_default_groups_organizationconfigsettings_permission.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0042_multiple_wireguard_tunnels.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0043_devicegroup_templates.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0044_config_error_reason.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0045_alter_vpn_webhook_endpoint.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0046_organizationlimits.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0047_add_organizationlimits.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0048_wifi_radio_band_migration.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0050_alter_vpnclient_unique_together.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0052_vpn_node_network_id.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0053_vpnclient_secret.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0054_device__is_deactivated.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0055_alter_config_status.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0056_vpnclient_template.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0057_populate_vpnclient_template.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0058_alter_vpnclient_template.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0059_zerotier_templates_ow_zt_to_global.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0060_cleanup_api_task_notification_types.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0061_config_checksum_db.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/settings.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/signals.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/sortedm2m/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/sortedm2m/fields.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/sortedm2m/forms.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/admin.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/device-delete-confirmation.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/devicegroup.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/lib/advanced-mode.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/lib/img/jsoneditor-icons.svg +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/lib/jsonschema-ui.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/device-delete-confirmation.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/lib/advanced-mode.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/lib/jsonschema-ui.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/lib/tomorrow_night_bright.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/management_ip.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/preview.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/switcher.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/tabs.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/unsaved_changes.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/utils.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/vpn.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/widget.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/import_export/import-openwisp.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/sortedm2m/patch_sortedm2m.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/static/support.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tasks.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tasks_zerotier.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/change_device_group.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/change_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/change_list_device.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/clone_template_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device/change_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device/delete_confirmation.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device_recover_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/jsonschema-widget.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/preview.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/system_context.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/device_group/change_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/import_export/import.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/reversion/config/revision_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/pytest.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_api.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_config.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_controller.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_device_group.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_handlers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_notifications.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_tag.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_template.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/validators.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/widgets.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/api/serializers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/api/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/api/views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/base/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/base/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/channels/consumers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/channels/routing.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/commands.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/airos/snmp.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/exceptions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/openwrt/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/openwrt/snmp.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/openwrt/ssh.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/snmp.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/ssh.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/exceptions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0002_credentials_auto_add.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0003_default_group_permissions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0004_django3_1_upgrade.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0005_device_connection_failure_reason.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0006_name_unique_per_organization.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0007_command.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0008_remove_conflicting_deviceconnections.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0009_alter_deviceconnection_unique_together.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/schema.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/settings.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/signals.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/css/command-inline.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/css/credentials.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/js/commands.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/js/credentials.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/js/lib/reconnecting-websocket.min.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/base.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/pytest.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test-key.ed25519 +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test-key.rsa +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_api.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_command_utilities.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_notifications.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_selenium.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_snmp.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_ssh.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/widgets.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/context_processors.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/filters.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/serializers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/base/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/base/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/channels/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/channels/consumers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/channels/routing.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/exportable.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/0002_default_groups_permissions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/0003_alter_devicelocation_floorplan_location.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/templates/admin/widgets/foreign_key_raw_id.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/pytest.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_admin_inline.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_api.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/migrations.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/mixins.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/api/serializers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/api/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/api/views.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/base/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/base/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0002_add_organization_name.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0003_fill_organization_name.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0004_auto_20180106_1814.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0005_organizational_unit_name.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0006_add_x509_passphrase_field.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0007_default_groups_permissions.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0008_serial_number_length.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0009_common_name_maxlength_64.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0010_common_name_organization_unique.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0011_disallowed_blank_key_length_or_digest.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/static/admin/pki/js/show-org-field.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/templates/admin/pki/change_form.html +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/test_admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/test_api.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/test_models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/pki/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/routing.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/serializers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/settings.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/admin.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/apps.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/base/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/filters.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0002_default_group_migration.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0003_related_field_allow_blank.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0004_index_rule_on_delete.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0005_number_of_subnets_and_ips.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/device.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/settings.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/signals.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/static/subnet-division/css/subnet-division.css +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/static/subnet-division/js/subnet-division.js +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tasks.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/helpers.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/test_models.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/test_rule.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/tests/__init__.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/tests/mixins.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/tests/test_selenium.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/tests/test_users_integration.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/tests/test_utilities.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/tests/utils.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/urls.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/vpn_backends.py +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/SOURCES.txt +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/dependency_links.txt +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/not-zip-safe +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/requires.txt +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/top_level.txt +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/pyproject.toml +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/requirements.txt +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/setup.cfg +0 -0
- {openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/setup.py +0 -0
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
Version 1.2.3 [2026-04-09]
|
|
5
|
+
--------------------------
|
|
6
|
+
|
|
7
|
+
Bugfixes
|
|
8
|
+
~~~~~~~~
|
|
9
|
+
|
|
10
|
+
- Corrected initial field value assignment in ``AbstractDevice`` class
|
|
11
|
+
- Fixed duplicate template entries in Device admin
|
|
12
|
+
- Invalidate Config checksum after subnet provisioning
|
|
13
|
+
- Fixed concurrent ``update_config`` detection `#1204
|
|
14
|
+
<https://github.com/openwisp/openwisp-controller/issues/1204>`_
|
|
15
|
+
|
|
16
|
+
Version 1.2.2 [2026-03-06]
|
|
17
|
+
--------------------------
|
|
18
|
+
|
|
19
|
+
Changes
|
|
20
|
+
~~~~~~~
|
|
21
|
+
|
|
22
|
+
Other changes
|
|
23
|
+
+++++++++++++
|
|
24
|
+
|
|
25
|
+
- Improved help text of configuration variable fields
|
|
26
|
+
- Minor fixes in the test suite
|
|
27
|
+
|
|
4
28
|
Version 1.2.1 [2026-03-04]
|
|
5
29
|
--------------------------
|
|
6
30
|
|
{openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/config.py
RENAMED
|
@@ -94,10 +94,10 @@ class AbstractConfig(ChecksumCacheMixin, BaseConfig):
|
|
|
94
94
|
blank=True,
|
|
95
95
|
default=dict,
|
|
96
96
|
help_text=_(
|
|
97
|
-
"
|
|
98
|
-
'<a href="
|
|
99
|
-
'
|
|
100
|
-
"
|
|
97
|
+
"allows overriding "
|
|
98
|
+
'<a href="https://openwisp.io/docs/stable/controller/user/variables.html'
|
|
99
|
+
'" target="_blank">'
|
|
100
|
+
"configuration variables</a>"
|
|
101
101
|
),
|
|
102
102
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
103
103
|
dump_kwargs={"indent": 4},
|
{openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/device.py
RENAMED
|
@@ -325,9 +325,9 @@ class AbstractDevice(OrgMixin, BaseModel):
|
|
|
325
325
|
if not present_values:
|
|
326
326
|
return
|
|
327
327
|
self.refresh_from_db(fields=present_values.keys())
|
|
328
|
-
for field in
|
|
329
|
-
setattr(self, f"_initial_{field}", field)
|
|
330
|
-
setattr(self, field,
|
|
328
|
+
for field, value in present_values.items():
|
|
329
|
+
setattr(self, f"_initial_{field}", getattr(self, field))
|
|
330
|
+
setattr(self, field, value)
|
|
331
331
|
|
|
332
332
|
def _check_name_changed(self):
|
|
333
333
|
if self._initial_name == models.DEFERRED:
|
|
@@ -42,8 +42,9 @@ class AbstractDeviceGroup(OrgMixin, TimeStampedEditableModel):
|
|
|
42
42
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
43
43
|
dump_kwargs={"indent": 4},
|
|
44
44
|
help_text=_(
|
|
45
|
-
"
|
|
46
|
-
"
|
|
45
|
+
"Store custom metadata related to this group. This field is intended "
|
|
46
|
+
"for arbitrary data that does not affect device configuration and can "
|
|
47
|
+
"be retrieved via the REST API for integrations or external tools."
|
|
47
48
|
),
|
|
48
49
|
verbose_name=_("Metadata"),
|
|
49
50
|
)
|
|
@@ -53,8 +54,7 @@ class AbstractDeviceGroup(OrgMixin, TimeStampedEditableModel):
|
|
|
53
54
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
54
55
|
dump_kwargs={"indent": 4},
|
|
55
56
|
help_text=_(
|
|
56
|
-
"
|
|
57
|
-
' or to add "Configuration Variables" to the devices.'
|
|
57
|
+
"Define configuration variables available to all devices in this group"
|
|
58
58
|
),
|
|
59
59
|
verbose_name=_("Configuration Variables"),
|
|
60
60
|
)
|
|
@@ -37,7 +37,8 @@ class AbstractOrganizationConfigSettings(UUIDModel):
|
|
|
37
37
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
38
38
|
dump_kwargs={"indent": 4},
|
|
39
39
|
help_text=_(
|
|
40
|
-
|
|
40
|
+
"Define reusable configuration variables available "
|
|
41
|
+
"to all devices in this organization"
|
|
41
42
|
),
|
|
42
43
|
verbose_name=_("Configuration Variables"),
|
|
43
44
|
)
|
{openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/base/template.py
RENAMED
|
@@ -97,10 +97,9 @@ class AbstractTemplate(ShareableOrgMixinUniqueName, BaseConfig):
|
|
|
97
97
|
default=dict,
|
|
98
98
|
blank=True,
|
|
99
99
|
help_text=_(
|
|
100
|
-
"
|
|
101
|
-
"values
|
|
102
|
-
"
|
|
103
|
-
"be used during schema validation."
|
|
100
|
+
"Define default values for the variables used in this template. "
|
|
101
|
+
"These values are used during validation and when a variable is "
|
|
102
|
+
"not provided by the device, group, or organization."
|
|
104
103
|
),
|
|
105
104
|
load_kwargs={"object_pairs_hook": OrderedDict},
|
|
106
105
|
dump_kwargs={"indent": 4},
|
|
@@ -18,9 +18,10 @@ class Migration(migrations.Migration):
|
|
|
18
18
|
default=dict,
|
|
19
19
|
dump_kwargs={"ensure_ascii": False, "indent": 4},
|
|
20
20
|
help_text=(
|
|
21
|
-
|
|
22
|
-
'/
|
|
23
|
-
"
|
|
21
|
+
"allows overriding "
|
|
22
|
+
'<a href="https://openwisp.io/docs/stable/controller/user/variables.html' # noqa: E501
|
|
23
|
+
'" target="_blank">'
|
|
24
|
+
"configuration variables</a>"
|
|
24
25
|
),
|
|
25
26
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
26
27
|
),
|
|
@@ -18,9 +18,9 @@ class Migration(migrations.Migration):
|
|
|
18
18
|
default=dict,
|
|
19
19
|
dump_kwargs={"ensure_ascii": False, "indent": 4},
|
|
20
20
|
help_text=(
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
21
|
+
"Define default values for the variables used in this template. "
|
|
22
|
+
"These values are used during validation and when a variable is "
|
|
23
|
+
"not provided by the device, group, or organization."
|
|
24
24
|
),
|
|
25
25
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
26
26
|
verbose_name="Default Values",
|
|
@@ -62,9 +62,11 @@ class Migration(migrations.Migration):
|
|
|
62
62
|
dump_kwargs={"ensure_ascii": False, "indent": 4},
|
|
63
63
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
64
64
|
help_text=(
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
65
|
+
"Store custom metadata related to this group. "
|
|
66
|
+
"This field is intended for arbitrary data that "
|
|
67
|
+
"does not affect device configuration and can "
|
|
68
|
+
"be retrieved via the REST API for integrations "
|
|
69
|
+
"or external tools."
|
|
68
70
|
),
|
|
69
71
|
verbose_name="Metadata",
|
|
70
72
|
),
|
|
@@ -20,8 +20,8 @@ class Migration(migrations.Migration):
|
|
|
20
20
|
default=dict,
|
|
21
21
|
dump_kwargs={"ensure_ascii": False, "indent": 4},
|
|
22
22
|
help_text=(
|
|
23
|
-
"
|
|
24
|
-
|
|
23
|
+
"Define configuration variables available "
|
|
24
|
+
"to all devices in this group"
|
|
25
25
|
),
|
|
26
26
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
27
27
|
verbose_name="Configuration Variables",
|
|
@@ -20,8 +20,8 @@ class Migration(migrations.Migration):
|
|
|
20
20
|
default=dict,
|
|
21
21
|
dump_kwargs={"indent": 4},
|
|
22
22
|
help_text=(
|
|
23
|
-
|
|
24
|
-
"
|
|
23
|
+
"Define reusable configuration variables available "
|
|
24
|
+
"to all devices in this organization"
|
|
25
25
|
),
|
|
26
26
|
load_kwargs={"object_pairs_hook": collections.OrderedDict},
|
|
27
27
|
verbose_name="Configuration Variables",
|
|
@@ -138,7 +138,7 @@ django.jQuery(function ($) {
|
|
|
138
138
|
resetTemplateOptions();
|
|
139
139
|
var enabledTemplates = [],
|
|
140
140
|
sortedm2mUl = $("ul.sortedm2m-items:first"),
|
|
141
|
-
sortedm2mPrefixUl = $("ul.sortedm2m-items
|
|
141
|
+
sortedm2mPrefixUl = $("#config-empty ul.sortedm2m-items");
|
|
142
142
|
|
|
143
143
|
// Adds "li" elements for templates
|
|
144
144
|
Object.keys(data).forEach(function (templateId, index) {
|
|
@@ -529,6 +529,20 @@ class TestDevice(
|
|
|
529
529
|
with self.assertNumQueries(3):
|
|
530
530
|
device._check_changed_fields()
|
|
531
531
|
|
|
532
|
+
def test_deferred_fields_populated_correctly(self):
|
|
533
|
+
device = self._create_device(
|
|
534
|
+
name="deferred-test",
|
|
535
|
+
management_ip="10.0.0.1",
|
|
536
|
+
)
|
|
537
|
+
# Load the instance with deferred fields omitted
|
|
538
|
+
device = Device.objects.only("id").get(pk=device.pk)
|
|
539
|
+
device.management_ip = "10.0.0.55"
|
|
540
|
+
# Saving the device object will populate the deferred fields
|
|
541
|
+
device.save()
|
|
542
|
+
# Ensure `_initial_<field>` contains the actual value, not the field name
|
|
543
|
+
self.assertEqual(getattr(device, "_initial_management_ip"), "10.0.0.55")
|
|
544
|
+
self.assertNotEqual(getattr(device, "_initial_management_ip"), "management_ip")
|
|
545
|
+
|
|
532
546
|
def test_exceed_organization_device_limit(self):
|
|
533
547
|
org = self._get_org()
|
|
534
548
|
org.config_limits.device_limit = 1
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import time
|
|
2
2
|
|
|
3
|
+
from django.contrib.auth.models import Group, Permission
|
|
3
4
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
|
4
5
|
from django.test import tag
|
|
5
6
|
from django.urls.base import reverse
|
|
@@ -40,9 +41,17 @@ class SeleniumTestMixin(BaseSeleniumTestMixin):
|
|
|
40
41
|
hidden = hidden or []
|
|
41
42
|
visible = visible or []
|
|
42
43
|
for template in hidden:
|
|
43
|
-
self.wait_for_invisibility(
|
|
44
|
+
self.wait_for_invisibility(
|
|
45
|
+
By.XPATH,
|
|
46
|
+
f'//ul[contains(@class,"sortedm2m-items")]'
|
|
47
|
+
f'//input[@value="{template.id}"]',
|
|
48
|
+
)
|
|
44
49
|
for template in visible:
|
|
45
|
-
self.wait_for_visibility(
|
|
50
|
+
self.wait_for_visibility(
|
|
51
|
+
By.XPATH,
|
|
52
|
+
f'//ul[contains(@class,"sortedm2m-items")]'
|
|
53
|
+
f'//input[@value="{template.id}"]',
|
|
54
|
+
)
|
|
46
55
|
|
|
47
56
|
|
|
48
57
|
@tag("selenium_tests")
|
|
@@ -373,6 +382,93 @@ class TestDeviceAdmin(
|
|
|
373
382
|
self.assertEqual(config.templates.count(), 0)
|
|
374
383
|
self.assertEqual(config.status, "modified")
|
|
375
384
|
|
|
385
|
+
def test_relevant_templates_duplicates(self):
|
|
386
|
+
"""
|
|
387
|
+
Test that a user with specific permissions can see shared templates
|
|
388
|
+
properly. Verifies that:
|
|
389
|
+
1. User with custom group permissions can access the admin
|
|
390
|
+
2. Multiple shared templates are displayed correctly
|
|
391
|
+
3. Each template appears only once in the sortedm2m list
|
|
392
|
+
"""
|
|
393
|
+
# Define permission codenames for the custom group
|
|
394
|
+
permission_codenames = [
|
|
395
|
+
"view_group",
|
|
396
|
+
"change_config",
|
|
397
|
+
"view_config",
|
|
398
|
+
"add_device",
|
|
399
|
+
"change_device",
|
|
400
|
+
"delete_device",
|
|
401
|
+
"view_device",
|
|
402
|
+
"view_devicegroup",
|
|
403
|
+
"view_template",
|
|
404
|
+
]
|
|
405
|
+
# Create a custom group with the specified permissions
|
|
406
|
+
permissions = Permission.objects.filter(codename__in=permission_codenames)
|
|
407
|
+
custom_group, _ = Group.objects.get_or_create(name="Custom Operator")
|
|
408
|
+
custom_group.permissions.set(permissions)
|
|
409
|
+
# Create a user and assign the custom group
|
|
410
|
+
user = self._create_user(
|
|
411
|
+
username="limited_user",
|
|
412
|
+
password="testpass123",
|
|
413
|
+
email="limited@test.com",
|
|
414
|
+
is_staff=True,
|
|
415
|
+
)
|
|
416
|
+
user.groups.add(custom_group)
|
|
417
|
+
org = self._get_org()
|
|
418
|
+
self._create_org_user(user=user, organization=org, is_admin=True)
|
|
419
|
+
# Create multiple shared templates (organization=None)
|
|
420
|
+
template1 = self._create_template(
|
|
421
|
+
name="Shared Template 1", organization=None, default=True
|
|
422
|
+
)
|
|
423
|
+
template2 = self._create_template(name="Shared Template 2", organization=None)
|
|
424
|
+
device = self._create_config(organization=org).device
|
|
425
|
+
# Login as the limited user
|
|
426
|
+
self.login(username="limited_user", password="testpass123")
|
|
427
|
+
# Navigate using Selenium
|
|
428
|
+
self.open(
|
|
429
|
+
reverse("admin:config_device_change", args=[device.id]) + "#config-group"
|
|
430
|
+
)
|
|
431
|
+
self.hide_loading_overlay()
|
|
432
|
+
with self.subTest(
|
|
433
|
+
"Regression precondition: empty Config inline is not rendered"
|
|
434
|
+
):
|
|
435
|
+
self.assertFalse(self.web_driver.find_elements(By.ID, "config-empty"))
|
|
436
|
+
|
|
437
|
+
with self.subTest("All shared templates should be visible"):
|
|
438
|
+
self._verify_templates_visibility(visible=[template1, template2])
|
|
439
|
+
|
|
440
|
+
with self.subTest("Verify sortedm2m list has exactly 2 template items"):
|
|
441
|
+
# Check that ul.sortedm2m-items.sortedm2m.ui-sortable has exactly 2 children
|
|
442
|
+
# with .sortedm2m-item class
|
|
443
|
+
sortedm2m_items = self.find_elements(
|
|
444
|
+
by=By.CSS_SELECTOR,
|
|
445
|
+
value="ul.sortedm2m-items.sortedm2m.ui-sortable > li.sortedm2m-item",
|
|
446
|
+
)
|
|
447
|
+
self.assertEqual(
|
|
448
|
+
len(sortedm2m_items),
|
|
449
|
+
2,
|
|
450
|
+
(
|
|
451
|
+
"Expected exactly 2 template items in sortedm2m list,"
|
|
452
|
+
f" found {len(sortedm2m_items)}"
|
|
453
|
+
),
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
with self.subTest(
|
|
457
|
+
"Verify checkbox inputs are rendered with expected attributes"
|
|
458
|
+
):
|
|
459
|
+
for idx, template_id in enumerate([template1.id, template2.id]):
|
|
460
|
+
checkbox = self.find_element(
|
|
461
|
+
by=By.ID, value=f"id_config-templates_{idx}"
|
|
462
|
+
)
|
|
463
|
+
self.assertEqual(checkbox.get_attribute("value"), str(template_id))
|
|
464
|
+
self.assertEqual(checkbox.get_attribute("data-required"), "false")
|
|
465
|
+
|
|
466
|
+
with self.subTest("Save operation completes successfully"):
|
|
467
|
+
# Scroll to the top of the page to ensure the save button is visible
|
|
468
|
+
self.web_driver.execute_script("window.scrollTo(0, 0);")
|
|
469
|
+
self.find_element(by=By.NAME, value="_save").click()
|
|
470
|
+
self.wait_for_presence(By.CSS_SELECTOR, ".messagelist .success", timeout=5)
|
|
471
|
+
|
|
376
472
|
|
|
377
473
|
@tag("selenium_tests")
|
|
378
474
|
class TestDeviceGroupAdmin(
|
{openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_vpn.py
RENAMED
|
@@ -3,12 +3,13 @@ import uuid
|
|
|
3
3
|
from subprocess import CalledProcessError, TimeoutExpired
|
|
4
4
|
from unittest import mock
|
|
5
5
|
|
|
6
|
+
import requests
|
|
6
7
|
from celery.exceptions import Retry, SoftTimeLimitExceeded
|
|
7
8
|
from django.conf import settings
|
|
8
9
|
from django.core.exceptions import ValidationError
|
|
9
10
|
from django.db.models.signals import post_save
|
|
10
11
|
from django.db.utils import IntegrityError
|
|
11
|
-
from django.http.response import HttpResponse
|
|
12
|
+
from django.http.response import HttpResponse
|
|
12
13
|
from django.test import TestCase, TransactionTestCase
|
|
13
14
|
from requests.exceptions import ConnectionError, RequestException, Timeout
|
|
14
15
|
from swapper import load_model
|
|
@@ -855,7 +856,7 @@ class TestWireguardTransaction(BaseTestVpn, TestWireguardVpnMixin, TransactionTe
|
|
|
855
856
|
fail_response.raise_for_status.side_effect = requests.exceptions.HTTPError(
|
|
856
857
|
"Not Found"
|
|
857
858
|
)
|
|
858
|
-
with mock.patch("logging.Logger.
|
|
859
|
+
with mock.patch("logging.Logger.error") as mocked_logger, mock.patch(
|
|
859
860
|
"requests.post", return_value=fail_response
|
|
860
861
|
):
|
|
861
862
|
post_save.send(
|
{openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/connection/tasks.py
RENAMED
|
@@ -16,20 +16,25 @@ logger = logging.getLogger(__name__)
|
|
|
16
16
|
_TASK_NAME = "openwisp_controller.connection.tasks.update_config"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def _is_update_in_progress(device_id):
|
|
19
|
+
def _is_update_in_progress(device_id, current_task_id=None):
|
|
20
20
|
active = current_app.control.inspect().active()
|
|
21
21
|
if not active:
|
|
22
22
|
return False
|
|
23
23
|
# check if there's any other running task before adding it
|
|
24
|
+
# exclude the current task by comparing task IDs
|
|
24
25
|
for task_list in active.values():
|
|
25
26
|
for task in task_list:
|
|
26
|
-
if
|
|
27
|
+
if (
|
|
28
|
+
task["name"] == _TASK_NAME
|
|
29
|
+
and str(device_id) in task["args"]
|
|
30
|
+
and task["id"] != current_task_id
|
|
31
|
+
):
|
|
27
32
|
return True
|
|
28
33
|
return False
|
|
29
34
|
|
|
30
35
|
|
|
31
|
-
@shared_task
|
|
32
|
-
def update_config(device_id):
|
|
36
|
+
@shared_task(bind=True)
|
|
37
|
+
def update_config(self, device_id):
|
|
33
38
|
"""
|
|
34
39
|
Launches the ``update_config()`` operation
|
|
35
40
|
of a specific device in the background
|
|
@@ -48,7 +53,7 @@ def update_config(device_id):
|
|
|
48
53
|
except ObjectDoesNotExist as e:
|
|
49
54
|
logger.warning(f'update_config("{device_id}") failed: {e}')
|
|
50
55
|
return
|
|
51
|
-
if _is_update_in_progress(device_id):
|
|
56
|
+
if _is_update_in_progress(device_id, current_task_id=self.request.id):
|
|
52
57
|
return
|
|
53
58
|
try:
|
|
54
59
|
device_conn = DeviceConnection.get_working_connection(device)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
from unittest import mock
|
|
3
3
|
from unittest.mock import PropertyMock
|
|
4
|
+
from uuid import uuid4
|
|
4
5
|
|
|
5
6
|
import paramiko
|
|
6
7
|
from django.contrib.auth.models import ContentType
|
|
@@ -1026,20 +1027,56 @@ class TestModelsTransaction(BaseTestModels, TransactionTestCase):
|
|
|
1026
1027
|
@mock.patch.object(DeviceConnection, "update_config")
|
|
1027
1028
|
@mock.patch.object(DeviceConnection, "get_working_connection")
|
|
1028
1029
|
def test_device_update_config_in_progress(
|
|
1029
|
-
self, mocked_get_working_connection,
|
|
1030
|
+
self, mocked_get_working_connection, mocked_update_config, mocked_sleep
|
|
1030
1031
|
):
|
|
1031
1032
|
conf = self._prepare_conf_object()
|
|
1032
1033
|
|
|
1033
|
-
with
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1034
|
+
with self.subTest("More than one update_config task active for the device"):
|
|
1035
|
+
with mock.patch("celery.app.control.Inspect.active") as mocked_active:
|
|
1036
|
+
mocked_active.return_value = {
|
|
1037
|
+
"task": [
|
|
1038
|
+
{
|
|
1039
|
+
"name": _TASK_NAME,
|
|
1040
|
+
"args": [str(conf.device.pk)],
|
|
1041
|
+
"id": str(uuid4()),
|
|
1042
|
+
}
|
|
1043
|
+
]
|
|
1044
|
+
}
|
|
1045
|
+
conf.config = {"general": {"timezone": "UTC"}}
|
|
1046
|
+
conf.full_clean()
|
|
1047
|
+
conf.save()
|
|
1048
|
+
mocked_active.assert_called_once()
|
|
1049
|
+
mocked_get_working_connection.assert_not_called()
|
|
1050
|
+
mocked_update_config.assert_not_called()
|
|
1051
|
+
|
|
1052
|
+
Config.objects.update(status="applied")
|
|
1053
|
+
mocked_get_working_connection.return_value = (
|
|
1054
|
+
conf.device.deviceconnection_set.first()
|
|
1055
|
+
)
|
|
1056
|
+
with self.subTest("Only one task is active for the device"):
|
|
1057
|
+
task_id = str(uuid4())
|
|
1058
|
+
with mock.patch(
|
|
1059
|
+
"celery.app.control.Inspect.active"
|
|
1060
|
+
) as mocked_active, mock.patch(
|
|
1061
|
+
"celery.app.task.Context.id",
|
|
1062
|
+
new_callable=mock.PropertyMock,
|
|
1063
|
+
return_value=task_id,
|
|
1064
|
+
):
|
|
1065
|
+
mocked_active.return_value = {
|
|
1066
|
+
"task": [
|
|
1067
|
+
{
|
|
1068
|
+
"name": _TASK_NAME,
|
|
1069
|
+
"args": [str(conf.device.pk)],
|
|
1070
|
+
"id": task_id,
|
|
1071
|
+
}
|
|
1072
|
+
]
|
|
1073
|
+
}
|
|
1074
|
+
conf.config = {"general": {"timezone": "Asia/Kolkata"}}
|
|
1075
|
+
conf.full_clean()
|
|
1076
|
+
conf.save()
|
|
1077
|
+
mocked_active.assert_called_once()
|
|
1078
|
+
mocked_get_working_connection.assert_called_once()
|
|
1079
|
+
mocked_update_config.assert_called_once()
|
|
1043
1080
|
|
|
1044
1081
|
@mock.patch("time.sleep")
|
|
1045
1082
|
@mock.patch.object(DeviceConnection, "update_config")
|
|
@@ -1053,8 +1090,15 @@ class TestModelsTransaction(BaseTestModels, TransactionTestCase):
|
|
|
1053
1090
|
)
|
|
1054
1091
|
|
|
1055
1092
|
with mock.patch("celery.app.control.Inspect.active") as mocked_active:
|
|
1093
|
+
# Mock a task running for a different device (args is different)
|
|
1056
1094
|
mocked_active.return_value = {
|
|
1057
|
-
"task": [
|
|
1095
|
+
"task": [
|
|
1096
|
+
{
|
|
1097
|
+
"name": _TASK_NAME,
|
|
1098
|
+
"args": ["another-device-id"], # Different device
|
|
1099
|
+
"id": "different-task-id",
|
|
1100
|
+
}
|
|
1101
|
+
]
|
|
1058
1102
|
}
|
|
1059
1103
|
conf.config = {"general": {"timezone": "UTC"}}
|
|
1060
1104
|
conf.full_clean()
|
|
@@ -19,6 +19,56 @@ class TestTasks(CreateConnectionsMixin, TestCase):
|
|
|
19
19
|
"openwisp_controller.connection.base.models.AbstractDeviceConnection.connect"
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
+
def _get_mocked_celery_active(self, device_id, task_id=None):
|
|
23
|
+
return {
|
|
24
|
+
"worker1": [
|
|
25
|
+
{
|
|
26
|
+
"name": tasks._TASK_NAME,
|
|
27
|
+
"args": [device_id],
|
|
28
|
+
"id": task_id or str(uuid.uuid4()),
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def test_is_update_in_progress_same_task(self):
|
|
34
|
+
device_id = str(uuid.uuid4())
|
|
35
|
+
task_id = str(uuid.uuid4())
|
|
36
|
+
with mock.patch(
|
|
37
|
+
"celery.app.control.Inspect.active",
|
|
38
|
+
return_value=self._get_mocked_celery_active(device_id, task_id),
|
|
39
|
+
):
|
|
40
|
+
result = tasks._is_update_in_progress(device_id, current_task_id=task_id)
|
|
41
|
+
self.assertEqual(result, False)
|
|
42
|
+
|
|
43
|
+
def test_is_update_in_progress_different_task(self):
|
|
44
|
+
device_id = str(uuid.uuid4())
|
|
45
|
+
current_task_id = str(uuid.uuid4())
|
|
46
|
+
other_task_id = str(uuid.uuid4())
|
|
47
|
+
with mock.patch(
|
|
48
|
+
"celery.app.control.Inspect.active",
|
|
49
|
+
return_value=self._get_mocked_celery_active(device_id, other_task_id),
|
|
50
|
+
):
|
|
51
|
+
result = tasks._is_update_in_progress(
|
|
52
|
+
device_id, current_task_id=current_task_id
|
|
53
|
+
)
|
|
54
|
+
self.assertEqual(result, True)
|
|
55
|
+
|
|
56
|
+
def test_is_update_in_progress_no_tasks(self):
|
|
57
|
+
device_id = str(uuid.uuid4())
|
|
58
|
+
with mock.patch("celery.app.control.Inspect.active", return_value={}):
|
|
59
|
+
result = tasks._is_update_in_progress(device_id)
|
|
60
|
+
self.assertEqual(result, False)
|
|
61
|
+
|
|
62
|
+
def test_is_update_in_progress_different_device(self):
|
|
63
|
+
device_id = str(uuid.uuid4())
|
|
64
|
+
other_device_id = str(uuid.uuid4())
|
|
65
|
+
with mock.patch(
|
|
66
|
+
"celery.app.control.Inspect.active",
|
|
67
|
+
return_value=self._get_mocked_celery_active(other_device_id),
|
|
68
|
+
):
|
|
69
|
+
result = tasks._is_update_in_progress(device_id)
|
|
70
|
+
self.assertEqual(result, False)
|
|
71
|
+
|
|
22
72
|
@mock.patch("logging.Logger.warning")
|
|
23
73
|
@mock.patch("time.sleep")
|
|
24
74
|
def test_update_config_missing_config(self, mocked_sleep, mocked_warning):
|
{openwisp_controller-1.2.1 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_admin.py
RENAMED
|
@@ -29,6 +29,12 @@ class TestAdmin(TestAdminMixin, TestGeoMixin, BaseTestAdmin, TestCase):
|
|
|
29
29
|
"""override TestAdminMixin.setUp"""
|
|
30
30
|
pass
|
|
31
31
|
|
|
32
|
+
def _get_location_add_params(self, **kwargs):
|
|
33
|
+
params = super()._get_location_add_params(**kwargs)
|
|
34
|
+
if "organization" not in kwargs:
|
|
35
|
+
params["organization"] = self._get_org().id
|
|
36
|
+
return params
|
|
37
|
+
|
|
32
38
|
def _create_multitenancy_test_env(self, vpn=False):
|
|
33
39
|
org1 = self._create_organization(name="test1org")
|
|
34
40
|
org2 = self._create_organization(name="test2org")
|
|
@@ -91,7 +91,11 @@ class TestDeviceAdminReadonly(
|
|
|
91
91
|
def test_unsaved_changes_readonly(self):
|
|
92
92
|
self.login()
|
|
93
93
|
ol = self._create_object_location()
|
|
94
|
-
path = reverse(
|
|
94
|
+
path = reverse(
|
|
95
|
+
f"admin:{self.object_model._meta.app_label}_"
|
|
96
|
+
f"{self.object_model._meta.model_name}_change",
|
|
97
|
+
args=[ol.device.id],
|
|
98
|
+
)
|
|
95
99
|
|
|
96
100
|
with self.subTest("Alert should not be displayed without any change"):
|
|
97
101
|
self.open(path)
|
|
@@ -79,16 +79,36 @@ class BaseSubnetDivisionRuleType(object):
|
|
|
79
79
|
def destroyer_receiver(cls, instance, **kwargs):
|
|
80
80
|
cls.destroy_provisioned_subnets_ips(instance, **kwargs)
|
|
81
81
|
|
|
82
|
-
@
|
|
83
|
-
def post_provision_handler(instance, provisioned, **kwargs):
|
|
82
|
+
@classmethod
|
|
83
|
+
def post_provision_handler(cls, instance, provisioned, **kwargs):
|
|
84
84
|
"""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
Hook for post-provisioning actions on subnets and IP addresses.
|
|
86
|
+
|
|
87
|
+
This method is intended to be extended by subclasses of rule types
|
|
88
|
+
to perform custom operations after subnets and IPs are provisioned.
|
|
89
|
+
|
|
90
|
+
Subnet provisioning is executed asynchronously in Celery workers.
|
|
91
|
+
If the device configuration references variables provided by the
|
|
92
|
+
subnet division rule, the current checksum may have been computed
|
|
93
|
+
using variable names instead of their provisioned values. In such cases,
|
|
94
|
+
`Config.checksum_db` (which tracks persisted configuration changes)
|
|
95
|
+
must be updated to reflect the actual provisioned values, and the
|
|
96
|
+
checksum cache invalidated to avoid stale data.
|
|
97
|
+
|
|
98
|
+
:param instance: The object that triggered the provisioning.
|
|
99
|
+
:param provisioned: Dictionary containing provisioned subnets and IPs,
|
|
100
|
+
or None if no provisioning occurred.
|
|
90
101
|
"""
|
|
91
|
-
|
|
102
|
+
if not provisioned:
|
|
103
|
+
return
|
|
104
|
+
config = cls.get_config(instance)
|
|
105
|
+
config._invalidate_backend_instance_cache()
|
|
106
|
+
current_checksum = config.checksum
|
|
107
|
+
if current_checksum != config.checksum_db:
|
|
108
|
+
# Update checksum using the UPDATE query to avoid sending
|
|
109
|
+
# unnecessary signals that may be triggered by `save()` method.
|
|
110
|
+
config._update_checksum_db(current_checksum)
|
|
111
|
+
config.invalidate_checksum_cache()
|
|
92
112
|
|
|
93
113
|
@staticmethod
|
|
94
114
|
def subnet_provisioned_signal_emitter(instance, provisioned):
|