openwisp-controller 1.2.2__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.2 → openwisp_controller-1.2.3}/CHANGES.rst +12 -0
- {openwisp_controller-1.2.2/openwisp_controller.egg-info → openwisp_controller-1.2.3}/PKG-INFO +1 -1
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/__init__.py +1 -1
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/device.py +3 -3
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/relevant_templates.js +1 -1
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_device.py +14 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_selenium.py +98 -2
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tasks.py +10 -5
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_models.py +56 -12
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_tasks.py +50 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_admin.py +6 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_selenium.py +5 -1
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/base.py +28 -8
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/vpn.py +3 -2
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/test_admin.py +137 -3
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3/openwisp_controller.egg-info}/PKG-INFO +1 -1
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/LICENSE +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/MANIFEST.in +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/README.rst +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/base.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/checks.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/download_views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/filters.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/serializers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/api/zerotier_service.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/base.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/channels_consumer.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/config.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/device_group.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/multitenancy.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/tag.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/template.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/base/vpn.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/controller/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/controller/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/controller/views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/crypto.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/exceptions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/exportable.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/filters.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/fixtures/test_templates.json +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/handlers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0003_template_tags.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0004_add_device_model.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0005_populate_device.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0006_config_device_not_null.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0007_simplify_config.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0008_update_indexes.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0009_device_system.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0010_auto_20180106_1814.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0011_update_device_mac_address.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0012_auto_20180219_1501.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0013_last_ip_management_ip_and_status_applied.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0014_device_hardware_id.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0015_default_groups_permissions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0016_default_organization_config_settings.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0017_template_name_organization_unique_together.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0018_config_context.py +0 -0
- {openwisp_controller-1.2.2 → 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.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0020_remove_config_organization.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0021_vpn_key.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0022_vpn_format_dh.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0023_update_context.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0024_update_context_data.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0025_update_device_key.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0026_hardware_id_not_unique.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0027_add_indexes_on_ip_fields.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0028_template_default_values.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0029_merge_django_netjsonconfig.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0030_django_taggit_update.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0031_update_vpn_dh_param.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0032_update_legacy_vpn_backend.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0033_name_unique_per_organization.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0034_template_required.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0035_device_name_unique_optional.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0036_device_group.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0037_alter_taggedtemplate.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0038_vpn_subnet.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0039_wireguard_vxlan_ipam.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0040_vpnclient_ip_setnull.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0041_default_groups_organizationconfigsettings_permission.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0042_multiple_wireguard_tunnels.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0043_devicegroup_templates.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0044_config_error_reason.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0045_alter_vpn_webhook_endpoint.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0046_organizationlimits.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0047_add_organizationlimits.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0048_wifi_radio_band_migration.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0049_devicegroup_context.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0050_alter_vpnclient_unique_together.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0051_organizationconfigsettings_context.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0052_vpn_node_network_id.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0053_vpnclient_secret.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0054_device__is_deactivated.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0055_alter_config_status.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0056_vpnclient_template.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0057_populate_vpnclient_template.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0058_alter_vpnclient_template.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0059_zerotier_templates_ow_zt_to_global.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0060_cleanup_api_task_notification_types.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/0061_config_checksum_db.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/settings.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/signals.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/sortedm2m/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/sortedm2m/fields.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/sortedm2m/forms.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/admin.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/device-delete-confirmation.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/devicegroup.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/lib/advanced-mode.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/lib/img/jsoneditor-icons.svg +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/css/lib/jsonschema-ui.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/device-delete-confirmation.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/lib/advanced-mode.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/lib/jsonschema-ui.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/lib/tomorrow_night_bright.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/management_ip.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/preview.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/switcher.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/tabs.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/unsaved_changes.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/utils.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/vpn.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/config/js/widget.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/import_export/import-openwisp.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/sortedm2m/patch_sortedm2m.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/static/support.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tasks.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tasks_zerotier.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/change_device_group.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/change_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/change_list_device.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/clone_template_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device/change_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device/delete_confirmation.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/device_recover_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/jsonschema-widget.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/preview.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/config/system_context.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/device_group/change_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/admin/import_export/import.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/templates/reversion/config/revision_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/pytest.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_api.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_config.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_controller.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_device_group.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_handlers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_notifications.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_tag.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_template.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/test_vpn.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/tests/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/validators.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/widgets.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/api/serializers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/api/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/api/views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/base/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/base/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/channels/consumers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/channels/routing.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/commands.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/airos/snmp.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/exceptions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/openwrt/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/openwrt/snmp.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/openwrt/ssh.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/snmp.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/connectors/ssh.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/exceptions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0002_credentials_auto_add.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0003_default_group_permissions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0004_django3_1_upgrade.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0005_device_connection_failure_reason.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0006_name_unique_per_organization.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0007_command.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0008_remove_conflicting_deviceconnections.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/0009_alter_deviceconnection_unique_together.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/schema.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/settings.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/signals.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/css/command-inline.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/css/credentials.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/js/commands.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/js/credentials.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/static/connection/js/lib/reconnecting-websocket.min.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/base.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/pytest.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test-key.ed25519 +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test-key.rsa +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_api.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_command_utilities.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_notifications.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_selenium.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_snmp.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/test_ssh.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/tests/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/connection/widgets.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/context_processors.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/filters.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/serializers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/api/views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/base/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/base/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/channels/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/channels/consumers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/channels/routing.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/exportable.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/0002_default_groups_permissions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/0003_alter_devicelocation_floorplan_location.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/templates/admin/widgets/foreign_key_raw_id.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/pytest.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_admin_inline.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_api.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/test_models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/tests/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/geo/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/migrations.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/mixins.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/api/serializers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/api/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/api/views.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/base/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/base/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0002_add_organization_name.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0003_fill_organization_name.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0004_auto_20180106_1814.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0005_organizational_unit_name.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0006_add_x509_passphrase_field.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0007_default_groups_permissions.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0008_serial_number_length.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0009_common_name_maxlength_64.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0010_common_name_organization_unique.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/0011_disallowed_blank_key_length_or_digest.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/static/admin/pki/js/show-org-field.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/templates/admin/pki/change_form.html +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/test_admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/test_api.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/test_models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/tests/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/pki/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/routing.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/serializers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/settings.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/admin.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/apps.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/base/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/filters.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0001_initial.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0002_default_group_migration.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0003_related_field_allow_blank.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0004_index_rule_on_delete.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/0005_number_of_subnets_and_ips.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/migrations/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/rule_types/device.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/settings.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/signals.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/static/subnet-division/css/subnet-division.css +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/static/subnet-division/js/subnet-division.js +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tasks.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/helpers.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/test_models.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/tests/test_rule.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/subnet_division/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/tests/__init__.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/tests/mixins.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/tests/test_selenium.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/tests/test_users_integration.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/tests/test_utilities.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/tests/utils.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/urls.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/vpn_backends.py +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/SOURCES.txt +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/dependency_links.txt +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/not-zip-safe +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/requires.txt +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller.egg-info/top_level.txt +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/pyproject.toml +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/requirements.txt +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/setup.cfg +0 -0
- {openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/setup.py +0 -0
|
@@ -1,6 +1,18 @@
|
|
|
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
|
+
|
|
4
16
|
Version 1.2.2 [2026-03-06]
|
|
5
17
|
--------------------------
|
|
6
18
|
|
{openwisp_controller-1.2.2 → 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:
|
|
@@ -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.2 → 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.2 → 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):
|
|
@@ -41,8 +41,9 @@ class VpnSubnetDivisionRuleType(BaseSubnetDivisionRuleType):
|
|
|
41
41
|
for vpn_client in qs:
|
|
42
42
|
cls.provision_receiver(instance=vpn_client, created=True)
|
|
43
43
|
|
|
44
|
-
@
|
|
45
|
-
def post_provision_handler(instance, provisioned, **kwargs):
|
|
44
|
+
@classmethod
|
|
45
|
+
def post_provision_handler(cls, instance, provisioned, **kwargs):
|
|
46
|
+
super().post_provision_handler(instance, provisioned, **kwargs)
|
|
46
47
|
# Assign the first provisioned IP address to the VPNClient
|
|
47
48
|
# only when subnets and IPs have been provisioned
|
|
48
49
|
if provisioned and provisioned["ip_addresses"]:
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
from unittest.mock import patch
|
|
2
2
|
|
|
3
|
-
from django.test import TestCase
|
|
3
|
+
from django.test import TestCase, TransactionTestCase
|
|
4
4
|
from django.urls import reverse
|
|
5
5
|
from swapper import load_model
|
|
6
6
|
|
|
7
|
-
from openwisp_controller.config.tests.
|
|
7
|
+
from openwisp_controller.config.tests.test_admin import TestDeviceAdminMixin
|
|
8
|
+
from openwisp_controller.config.tests.utils import (
|
|
9
|
+
TestVpnX509Mixin,
|
|
10
|
+
TestWireguardVpnMixin,
|
|
11
|
+
)
|
|
8
12
|
from openwisp_users.tests.utils import TestMultitenantAdminMixin
|
|
9
13
|
|
|
10
|
-
from .helpers import SubnetDivisionAdminTestMixin
|
|
14
|
+
from .helpers import SubnetDivisionAdminTestMixin, SubnetDivisionTestMixin
|
|
11
15
|
|
|
12
16
|
Subnet = load_model("openwisp_ipam", "Subnet")
|
|
13
17
|
Device = load_model("config", "Device")
|
|
18
|
+
Config = load_model("config", "Config")
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
class TestSubnetAdmin(
|
|
@@ -257,3 +262,132 @@ class TestDeviceAdmin(
|
|
|
257
262
|
)
|
|
258
263
|
self.assertEqual(subnet_response.status_code, 200)
|
|
259
264
|
self.assertContains(subnet_response, self.config.device.name, 1)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class TestTransactionDeviceAdmin(
|
|
268
|
+
SubnetDivisionTestMixin,
|
|
269
|
+
TestVpnX509Mixin,
|
|
270
|
+
TestDeviceAdminMixin,
|
|
271
|
+
TransactionTestCase,
|
|
272
|
+
):
|
|
273
|
+
ipam_label = "openwisp_ipam"
|
|
274
|
+
config_label = "config"
|
|
275
|
+
|
|
276
|
+
def test_vpn_template_switch_checksum_db(self):
|
|
277
|
+
admin = self._create_admin()
|
|
278
|
+
self.client.force_login(admin)
|
|
279
|
+
org = self._get_org()
|
|
280
|
+
vpn1_subnet = self._get_master_subnet(organization=org, subnet="10.0.0.0/24")
|
|
281
|
+
self._get_vpn_subdivision_rule(
|
|
282
|
+
number_of_ips=1,
|
|
283
|
+
number_of_subnets=1,
|
|
284
|
+
organization=org,
|
|
285
|
+
master_subnet=vpn1_subnet,
|
|
286
|
+
label="VPN1",
|
|
287
|
+
)
|
|
288
|
+
vpn1 = self._create_vpn(name="vpn1", organization=org, subnet=vpn1_subnet)
|
|
289
|
+
vpn2_subnet = self._get_master_subnet(organization=org, subnet="10.0.1.0/24")
|
|
290
|
+
self._get_vpn_subdivision_rule(
|
|
291
|
+
number_of_ips=1,
|
|
292
|
+
number_of_subnets=1,
|
|
293
|
+
organization=org,
|
|
294
|
+
master_subnet=vpn2_subnet,
|
|
295
|
+
label="VPN2",
|
|
296
|
+
)
|
|
297
|
+
vpn2 = self._create_vpn(name="vpn2", organization=org, subnet=vpn2_subnet)
|
|
298
|
+
vpn1_template = self._create_template(
|
|
299
|
+
organization=org,
|
|
300
|
+
name="vpn1-template",
|
|
301
|
+
type="vpn",
|
|
302
|
+
vpn=vpn1,
|
|
303
|
+
default_values={
|
|
304
|
+
"VPN1_subnet1_ip1": "10.0.0.1",
|
|
305
|
+
"VPN1_prefix": "24",
|
|
306
|
+
"ifname": "tun0",
|
|
307
|
+
},
|
|
308
|
+
auto_cert=True,
|
|
309
|
+
config={},
|
|
310
|
+
)
|
|
311
|
+
vpn1_template.config["openvpn"][0]["dev"] = "{{ ifname }}"
|
|
312
|
+
vpn1_template.config.update(
|
|
313
|
+
{
|
|
314
|
+
"network": [
|
|
315
|
+
{
|
|
316
|
+
"config_name": "interface",
|
|
317
|
+
"config_value": "lan",
|
|
318
|
+
"ipaddr": "{{ VPN1_subnet1_ip1 }}",
|
|
319
|
+
"netmask": "255.255.255.240",
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
vpn1_template.full_clean()
|
|
325
|
+
vpn1_template.save()
|
|
326
|
+
vpn2_template = self._create_template(
|
|
327
|
+
organization=org,
|
|
328
|
+
name="vpn2-template",
|
|
329
|
+
type="vpn",
|
|
330
|
+
vpn=vpn2,
|
|
331
|
+
default_values={
|
|
332
|
+
"VPN2_subnet1_ip1": "10.0.1.1",
|
|
333
|
+
"VPN2_prefix": "32",
|
|
334
|
+
"ifname": "tun1",
|
|
335
|
+
},
|
|
336
|
+
auto_cert=True,
|
|
337
|
+
config={},
|
|
338
|
+
)
|
|
339
|
+
vpn2_template.config["openvpn"][0]["dev"] = "{{ ifname }}"
|
|
340
|
+
vpn2_template.config.update(
|
|
341
|
+
{
|
|
342
|
+
"network": [
|
|
343
|
+
{
|
|
344
|
+
"config_name": "interface",
|
|
345
|
+
"config_value": "lan",
|
|
346
|
+
"ipaddr": "{{ VPN2_subnet1_ip1 }}",
|
|
347
|
+
"netmask": "255.255.255.240",
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
vpn2_template.full_clean()
|
|
353
|
+
vpn2_template.save()
|
|
354
|
+
default_template = self._create_template(
|
|
355
|
+
name="default-template",
|
|
356
|
+
default=True,
|
|
357
|
+
)
|
|
358
|
+
path = reverse(f"admin:{self.config_label}_device_add")
|
|
359
|
+
params = self._get_device_params(org=org)
|
|
360
|
+
params.update(
|
|
361
|
+
{"config-0-templates": f"{default_template.pk},{vpn1_template.pk}"}
|
|
362
|
+
)
|
|
363
|
+
response = self.client.post(path, data=params, follow=True)
|
|
364
|
+
self.assertEqual(response.status_code, 200)
|
|
365
|
+
config = Config.objects.get(device__name=params["name"])
|
|
366
|
+
config.refresh_from_db()
|
|
367
|
+
config._invalidate_backend_instance_cache()
|
|
368
|
+
initial_checksum = config.checksum
|
|
369
|
+
self.assertEqual(config.checksum_db, initial_checksum)
|
|
370
|
+
self.assertEqual(config.vpnclient_set.count(), 1)
|
|
371
|
+
self.assertEqual(config.vpnclient_set.first().vpn, vpn1)
|
|
372
|
+
|
|
373
|
+
path = reverse(
|
|
374
|
+
f"admin:{self.config_label}_device_change", args=[config.device_id]
|
|
375
|
+
)
|
|
376
|
+
params.update(
|
|
377
|
+
{
|
|
378
|
+
"config-0-templates": f"{default_template.pk},{vpn2_template.pk}",
|
|
379
|
+
"config-0-id": str(config.pk),
|
|
380
|
+
"config-0-device": str(config.device_id),
|
|
381
|
+
"config-INITIAL_FORMS": 1,
|
|
382
|
+
"_continue": True,
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
response = self.client.post(path, data=params, follow=True)
|
|
386
|
+
self.assertEqual(response.status_code, 200)
|
|
387
|
+
config.refresh_from_db()
|
|
388
|
+
config._invalidate_backend_instance_cache()
|
|
389
|
+
self.assertEqual(config.status, "modified")
|
|
390
|
+
self.assertEqual(config.vpnclient_set.count(), 1)
|
|
391
|
+
self.assertEqual(config.vpnclient_set.first().vpn, vpn2)
|
|
392
|
+
self.assertNotEqual(config.checksum, initial_checksum)
|
|
393
|
+
self.assertEqual(config.checksum, config.checksum_db)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openwisp_controller-1.2.2 → openwisp_controller-1.2.3}/openwisp_controller/config/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|