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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/CHANGES.rst +24 -7
  2. {openwisp_controller-1.1/openwisp_controller.egg-info → openwisp_controller-1.1.1}/PKG-INFO +26 -14
  3. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/README.rst +13 -12
  4. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/__init__.py +1 -1
  5. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/admin.py +48 -17
  6. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/views.py +4 -0
  7. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/device.py +1 -0
  8. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/template.py +9 -1
  9. openwisp_controller-1.1.1/openwisp_controller/config/static/config/css/device-delete-confirmation.css +11 -0
  10. openwisp_controller-1.1.1/openwisp_controller/config/static/config/js/device-delete-confirmation.js +12 -0
  11. openwisp_controller-1.1.1/openwisp_controller/config/templates/admin/config/device/delete_confirmation.html +81 -0
  12. openwisp_controller-1.1.1/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +87 -0
  13. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_admin.py +1 -2
  14. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_api.py +16 -0
  15. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_device.py +8 -0
  16. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_selenium.py +74 -0
  17. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_template.py +15 -0
  18. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/admin.py +34 -1
  19. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/test_api.py +1 -1
  20. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/admin.py +1 -1
  21. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/tests/test_selenium.py +20 -5
  22. {openwisp_controller-1.1 → openwisp_controller-1.1.1/openwisp_controller.egg-info}/PKG-INFO +26 -14
  23. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller.egg-info/SOURCES.txt +3 -0
  24. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/pyproject.toml +1 -1
  25. openwisp_controller-1.1/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +0 -36
  26. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/LICENSE +0 -0
  27. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/MANIFEST.in +0 -0
  28. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/admin.py +0 -0
  29. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/base.py +0 -0
  30. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/checks.py +0 -0
  31. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/__init__.py +0 -0
  32. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/__init__.py +0 -0
  33. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/download_views.py +0 -0
  34. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/filters.py +0 -0
  35. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/serializers.py +0 -0
  36. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/urls.py +0 -0
  37. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/api/zerotier_service.py +0 -0
  38. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/apps.py +0 -0
  39. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/__init__.py +0 -0
  40. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/base.py +0 -0
  41. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/channels_consumer.py +0 -0
  42. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/config.py +0 -0
  43. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/device_group.py +0 -0
  44. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/multitenancy.py +0 -0
  45. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/tag.py +0 -0
  46. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/base/vpn.py +0 -0
  47. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/controller/__init__.py +0 -0
  48. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/controller/urls.py +0 -0
  49. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/controller/views.py +0 -0
  50. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/crypto.py +0 -0
  51. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/exceptions.py +0 -0
  52. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/exportable.py +0 -0
  53. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/filters.py +0 -0
  54. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/fixtures/test_templates.json +0 -0
  55. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/handlers.py +0 -0
  56. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py +0 -0
  57. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0003_template_tags.py +0 -0
  58. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0004_add_device_model.py +0 -0
  59. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0005_populate_device.py +0 -0
  60. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0006_config_device_not_null.py +0 -0
  61. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0007_simplify_config.py +0 -0
  62. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0008_update_indexes.py +0 -0
  63. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0009_device_system.py +0 -0
  64. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0010_auto_20180106_1814.py +0 -0
  65. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0011_update_device_mac_address.py +0 -0
  66. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0012_auto_20180219_1501.py +0 -0
  67. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0013_last_ip_management_ip_and_status_applied.py +0 -0
  68. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0014_device_hardware_id.py +0 -0
  69. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0015_default_groups_permissions.py +0 -0
  70. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0016_default_organization_config_settings.py +0 -0
  71. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0017_template_name_organization_unique_together.py +0 -0
  72. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0018_config_context.py +0 -0
  73. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0019_organization_mac_add_hardware_id_name_unique_together.py +0 -0
  74. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0020_remove_config_organization.py +0 -0
  75. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0021_vpn_key.py +0 -0
  76. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0022_vpn_format_dh.py +0 -0
  77. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0023_update_context.py +0 -0
  78. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0024_update_context_data.py +0 -0
  79. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0025_update_device_key.py +0 -0
  80. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0026_hardware_id_not_unique.py +0 -0
  81. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0027_add_indexes_on_ip_fields.py +0 -0
  82. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0028_template_default_values.py +0 -0
  83. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0029_merge_django_netjsonconfig.py +0 -0
  84. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0030_django_taggit_update.py +0 -0
  85. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0031_update_vpn_dh_param.py +0 -0
  86. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0032_update_legacy_vpn_backend.py +0 -0
  87. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0033_name_unique_per_organization.py +0 -0
  88. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0034_template_required.py +0 -0
  89. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0035_device_name_unique_optional.py +0 -0
  90. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0036_device_group.py +0 -0
  91. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0037_alter_taggedtemplate.py +0 -0
  92. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0038_vpn_subnet.py +0 -0
  93. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0039_wireguard_vxlan_ipam.py +0 -0
  94. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0040_vpnclient_ip_setnull.py +0 -0
  95. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0041_default_groups_organizationconfigsettings_permission.py +0 -0
  96. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0042_multiple_wireguard_tunnels.py +0 -0
  97. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0043_devicegroup_templates.py +0 -0
  98. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0044_config_error_reason.py +0 -0
  99. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0045_alter_vpn_webhook_endpoint.py +0 -0
  100. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0046_organizationlimits.py +0 -0
  101. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0047_add_organizationlimits.py +0 -0
  102. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0048_wifi_radio_band_migration.py +0 -0
  103. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0049_devicegroup_context.py +0 -0
  104. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0050_alter_vpnclient_unique_together.py +0 -0
  105. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0051_organizationconfigsettings_context.py +0 -0
  106. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0052_vpn_node_network_id.py +0 -0
  107. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0053_vpnclient_secret.py +0 -0
  108. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0054_device__is_deactivated.py +0 -0
  109. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/0055_alter_config_status.py +0 -0
  110. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/migrations/__init__.py +0 -0
  111. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/models.py +0 -0
  112. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/settings.py +0 -0
  113. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/signals.py +0 -0
  114. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/sortedm2m/__init__.py +0 -0
  115. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/sortedm2m/fields.py +0 -0
  116. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/sortedm2m/forms.py +0 -0
  117. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/css/admin.css +0 -0
  118. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/css/devicegroup.css +0 -0
  119. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/css/lib/advanced-mode.css +0 -0
  120. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/css/lib/img/jsoneditor-icons.svg +0 -0
  121. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/css/lib/jsonschema-ui.css +0 -0
  122. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/lib/advanced-mode.js +0 -0
  123. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/lib/jsonschema-ui.js +0 -0
  124. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/lib/tomorrow_night_bright.js +0 -0
  125. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/management_ip.js +0 -0
  126. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/preview.js +0 -0
  127. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/relevant_templates.js +0 -0
  128. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/switcher.js +0 -0
  129. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/tabs.js +0 -0
  130. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/unsaved_changes.js +0 -0
  131. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/utils.js +0 -0
  132. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/vpn.js +0 -0
  133. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/config/js/widget.js +0 -0
  134. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/import_export/import-openwisp.css +0 -0
  135. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/sortedm2m/patch_sortedm2m.js +0 -0
  136. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/static/support.css +0 -0
  137. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tasks.py +0 -0
  138. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tasks_zerotier.py +0 -0
  139. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/change_device_group.html +0 -0
  140. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/change_form.html +0 -0
  141. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/change_list_device.html +0 -0
  142. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/clone_template_form.html +0 -0
  143. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/device/change_form.html +0 -0
  144. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/device_recover_form.html +0 -0
  145. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/jsonschema-widget.html +0 -0
  146. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/preview.html +0 -0
  147. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/config/system_context.html +0 -0
  148. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/device_group/change_form.html +0 -0
  149. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/admin/import_export/import.html +0 -0
  150. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/templates/reversion/config/revision_form.html +0 -0
  151. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/__init__.py +0 -0
  152. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/pytest.py +0 -0
  153. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_apps.py +0 -0
  154. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_config.py +0 -0
  155. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_controller.py +0 -0
  156. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_device_group.py +0 -0
  157. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_handlers.py +0 -0
  158. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_notifications.py +0 -0
  159. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_tag.py +0 -0
  160. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_views.py +0 -0
  161. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/test_vpn.py +0 -0
  162. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/tests/utils.py +0 -0
  163. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/urls.py +0 -0
  164. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/utils.py +0 -0
  165. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/validators.py +0 -0
  166. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/views.py +0 -0
  167. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/config/widgets.py +0 -0
  168. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/__init__.py +0 -0
  169. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/admin.py +0 -0
  170. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/api/serializer.py +0 -0
  171. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/api/urls.py +0 -0
  172. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/api/views.py +0 -0
  173. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/apps.py +0 -0
  174. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/base/__init__.py +0 -0
  175. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/base/models.py +0 -0
  176. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/channels/consumers.py +0 -0
  177. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/channels/routing.py +0 -0
  178. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/commands.py +0 -0
  179. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/__init__.py +0 -0
  180. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/airos/snmp.py +0 -0
  181. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/exceptions.py +0 -0
  182. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/openwrt/__init__.py +0 -0
  183. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/openwrt/snmp.py +0 -0
  184. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/openwrt/ssh.py +0 -0
  185. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/snmp.py +0 -0
  186. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/connectors/ssh.py +0 -0
  187. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/exceptions.py +0 -0
  188. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0001_initial.py +0 -0
  189. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0002_credentials_auto_add.py +0 -0
  190. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0003_default_group_permissions.py +0 -0
  191. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0004_django3_1_upgrade.py +0 -0
  192. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0005_device_connection_failure_reason.py +0 -0
  193. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0006_name_unique_per_organization.py +0 -0
  194. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0007_command.py +0 -0
  195. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0008_remove_conflicting_deviceconnections.py +0 -0
  196. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/0009_alter_deviceconnection_unique_together.py +0 -0
  197. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/migrations/__init__.py +0 -0
  198. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/models.py +0 -0
  199. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/schema.py +0 -0
  200. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/settings.py +0 -0
  201. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/signals.py +0 -0
  202. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/static/connection/css/command-inline.css +0 -0
  203. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/static/connection/css/credentials.css +0 -0
  204. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/static/connection/js/commands.js +0 -0
  205. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/static/connection/js/credentials.js +0 -0
  206. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/static/connection/js/lib/reconnecting-websocket.min.js +0 -0
  207. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tasks.py +0 -0
  208. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/__init__.py +0 -0
  209. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/base.py +0 -0
  210. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/pytest.py +0 -0
  211. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test-key.ed25519 +0 -0
  212. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test-key.rsa +0 -0
  213. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_admin.py +0 -0
  214. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_api.py +0 -0
  215. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_command_utilities.py +0 -0
  216. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_models.py +0 -0
  217. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_notifications.py +0 -0
  218. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_selenium.py +0 -0
  219. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_snmp.py +0 -0
  220. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_ssh.py +0 -0
  221. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/test_tasks.py +0 -0
  222. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/tests/utils.py +0 -0
  223. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/utils.py +0 -0
  224. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/connection/widgets.py +0 -0
  225. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/context_processors.py +0 -0
  226. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/__init__.py +0 -0
  227. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/api/__init__.py +0 -0
  228. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/api/filters.py +0 -0
  229. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/api/serializers.py +0 -0
  230. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/api/urls.py +0 -0
  231. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/api/views.py +0 -0
  232. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/apps.py +0 -0
  233. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/base/__init__.py +0 -0
  234. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/base/models.py +0 -0
  235. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/channels/__init__.py +0 -0
  236. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/channels/consumers.py +0 -0
  237. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/channels/routing.py +0 -0
  238. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/exportable.py +0 -0
  239. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/migrations/0001_initial.py +0 -0
  240. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/migrations/0002_default_groups_permissions.py +0 -0
  241. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/migrations/0003_alter_devicelocation_floorplan_location.py +0 -0
  242. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/migrations/__init__.py +0 -0
  243. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/models.py +0 -0
  244. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/templates/admin/widgets/foreign_key_raw_id.html +0 -0
  245. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/__init__.py +0 -0
  246. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/pytest.py +0 -0
  247. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/test_admin.py +0 -0
  248. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/test_admin_inline.py +0 -0
  249. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/test_apps.py +0 -0
  250. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/test_models.py +0 -0
  251. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/tests/utils.py +0 -0
  252. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/geo/utils.py +0 -0
  253. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/migrations.py +0 -0
  254. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/mixins.py +0 -0
  255. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/__init__.py +0 -0
  256. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/admin.py +0 -0
  257. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/api/serializers.py +0 -0
  258. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/api/urls.py +0 -0
  259. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/api/views.py +0 -0
  260. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/apps.py +0 -0
  261. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/base/__init__.py +0 -0
  262. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/base/models.py +0 -0
  263. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0001_initial.py +0 -0
  264. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0002_add_organization_name.py +0 -0
  265. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0003_fill_organization_name.py +0 -0
  266. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0004_auto_20180106_1814.py +0 -0
  267. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0005_organizational_unit_name.py +0 -0
  268. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0006_add_x509_passphrase_field.py +0 -0
  269. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0007_default_groups_permissions.py +0 -0
  270. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0008_serial_number_length.py +0 -0
  271. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0009_common_name_maxlength_64.py +0 -0
  272. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0010_common_name_organization_unique.py +0 -0
  273. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/0011_disallowed_blank_key_length_or_digest.py +0 -0
  274. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/migrations/__init__.py +0 -0
  275. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/models.py +0 -0
  276. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/static/admin/pki/js/show-org-field.js +0 -0
  277. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/templates/admin/pki/change_form.html +0 -0
  278. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/tests/__init__.py +0 -0
  279. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/tests/test_admin.py +0 -0
  280. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/tests/test_api.py +0 -0
  281. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/tests/test_models.py +0 -0
  282. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/tests/utils.py +0 -0
  283. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/urls.py +0 -0
  284. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/pki/utils.py +0 -0
  285. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/routing.py +0 -0
  286. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/settings.py +0 -0
  287. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/__init__.py +0 -0
  288. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/apps.py +0 -0
  289. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/base/models.py +0 -0
  290. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/filters.py +0 -0
  291. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/migrations/0001_initial.py +0 -0
  292. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/migrations/0002_default_group_migration.py +0 -0
  293. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/migrations/0003_related_field_allow_blank.py +0 -0
  294. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/migrations/0004_index_rule_on_delete.py +0 -0
  295. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/migrations/0005_number_of_subnets_and_ips.py +0 -0
  296. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/migrations/__init__.py +0 -0
  297. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/models.py +0 -0
  298. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/rule_types/__init__.py +0 -0
  299. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/rule_types/base.py +0 -0
  300. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/rule_types/device.py +0 -0
  301. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/rule_types/vpn.py +0 -0
  302. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/settings.py +0 -0
  303. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/signals.py +0 -0
  304. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/static/subnet-division/css/subnet-division.css +0 -0
  305. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/static/subnet-division/js/subnet-division.js +0 -0
  306. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/tasks.py +0 -0
  307. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/tests/__init__.py +0 -0
  308. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/tests/helpers.py +0 -0
  309. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/tests/test_admin.py +0 -0
  310. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/tests/test_models.py +0 -0
  311. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/tests/test_rule.py +0 -0
  312. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/subnet_division/utils.py +0 -0
  313. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/tests/__init__.py +0 -0
  314. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/tests/mixins.py +0 -0
  315. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/tests/test_users_integration.py +0 -0
  316. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/tests/test_utilities.py +0 -0
  317. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/tests/utils.py +0 -0
  318. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/urls.py +0 -0
  319. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller/vpn_backends.py +0 -0
  320. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller.egg-info/dependency_links.txt +0 -0
  321. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller.egg-info/not-zip-safe +0 -0
  322. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller.egg-info/requires.txt +0 -0
  323. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/openwisp_controller.egg-info/top_level.txt +0 -0
  324. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/requirements.txt +0 -0
  325. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/setup.cfg +0 -0
  326. {openwisp_controller-1.1 → openwisp_controller-1.1.1}/setup.py +0 -0
@@ -1,6 +1,23 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ Version 1.1.1 [2025-01-31]
5
+ --------------------------
6
+
7
+ Bugfixes
8
+ ~~~~~~~~
9
+
10
+ - Fixed `recovering deleted device with related location
11
+ <https://github.com/openwisp/openwisp-controller/issues/936>`__.
12
+ - Fixed `deleting device with “deactivating” config status
13
+ <https://github.com/openwisp/openwisp-controller/issues/949>`__.
14
+ - Fixed `conversion of MAC address to uppercase format
15
+ <https://github.com/openwisp/openwisp-controller/issues/922>`__ with the
16
+ openwisp-config agent.
17
+ - Fixed `updating templates with invalid configurations to prevent
18
+ ValidationError due to cache invalidation mechanism
19
+ <https://github.com/openwisp/openwisp-controller/pull/948>`__.
20
+
4
21
  Version 1.1.0 [2024-11-22]
5
22
  --------------------------
6
23
 
@@ -8,14 +25,14 @@ Features
8
25
  ~~~~~~~~
9
26
 
10
27
  - Added `support for ZeroTier
11
- <https://openwisp.io/docs/dev/controller/user/zerotier.html>`_.
28
+ <https://openwisp.io/docs/stable/controller/user/zerotier.html>`_.
12
29
  - Added support for adding `configuration templates
13
- <https://openwisp.io/docs/dev/controller/user/device-groups.html#group-templates>`_
30
+ <https://openwisp.io/docs/stable/controller/user/device-groups.html#group-templates>`_
14
31
  and `configuration variables
15
- <https://openwisp.io/docs/dev/controller/user/device-groups.html#group-configuration-variables>`_
32
+ <https://openwisp.io/docs/stable/controller/user/device-groups.html#group-configuration-variables>`_
16
33
  to device groups.
17
34
  - Implemented support for `SNMP credentials
18
- <https://openwisp.io/docs/dev/controller/user/intro.html#snmp>`_.
35
+ <https://openwisp.io/docs/stable/controller/user/intro.html#snmp>`_.
19
36
  - Added support for device deactivation and reactivation.
20
37
  - Introduced an admin action to quickly move devices between groups.
21
38
  - Added autocomplete support for filters in the admin interface.
@@ -23,13 +40,13 @@ Features
23
40
  config agent.
24
41
  - Enabled filtering of subnets by Subnet Division Rule type.
25
42
  - Allowed `limiting the number of devices per organization
26
- <https://openwisp.io/docs/dev/controller/user/organization-limits.html>`_.
43
+ <https://openwisp.io/docs/stable/controller/user/organization-limits.html>`_.
27
44
  - Added support for defining organization-level variables.
28
45
  - Enabled `bulk import and export of devices
29
- <https://openwisp.io/docs/dev/controller/user/import-export.html>`_.
46
+ <https://openwisp.io/docs/stable/controller/user/import-export.html>`_.
30
47
  - Added `functionality to configure available commands for each
31
48
  organization
32
- <https://openwisp.io/docs/dev/controller/user/settings.html#openwisp-controller-organization-enabled-commands>`_.
49
+ <https://openwisp.io/docs/stable/controller/user/settings.html#openwisp-controller-organization-enabled-commands>`_.
33
50
  - Added a dashboard chart showing the distribution of devices across
34
51
  different groups.
35
52
  - Added filters to the REST API.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: openwisp-controller
3
- Version: 1.1
3
+ Version: 1.1.1
4
4
  Summary: OpenWISP Controller
5
5
  Home-page: http://openwisp.org
6
6
  Download-URL: https://github.com/openwisp/openwisp-controller/releases
@@ -37,6 +37,17 @@ Requires-Dist: django-cache-memoize~=0.2.0
37
37
  Requires-Dist: shortuuid~=1.0.13
38
38
  Requires-Dist: netaddr~=1.3.0
39
39
  Requires-Dist: django-import-export~=3.3.0
40
+ Dynamic: author
41
+ Dynamic: author-email
42
+ Dynamic: classifier
43
+ Dynamic: description
44
+ Dynamic: download-url
45
+ Dynamic: home-page
46
+ Dynamic: keywords
47
+ Dynamic: license
48
+ Dynamic: platform
49
+ Dynamic: requires-dist
50
+ Dynamic: summary
40
51
 
41
52
  openwisp-controller
42
53
  ===================
@@ -89,30 +100,31 @@ built on top of its building blocks.
89
100
 
90
101
  Other popular building blocks that are part of the OpenWISP ecosystem are:
91
102
 
92
- - `openwisp-monitoring <https://openwisp.io/docs/dev/monitoring/>`_:
103
+ - `openwisp-monitoring <https://openwisp.io/docs/stable/monitoring/>`_:
93
104
  provides device status monitoring, collection of metrics, charts,
94
105
  alerts, possibility to define custom checks
95
106
  - `openwisp-firmware-upgrader
96
- <https://openwisp.io/docs/dev/firmware-upgrader/>`_: automated firmware
97
- upgrades (single devices or mass network upgrades)
98
- - `openwisp-radius <https://openwisp.io/docs/dev/user/radius.html>`_:
107
+ <https://openwisp.io/docs/stable/firmware-upgrader/>`_: automated
108
+ firmware upgrades (single devices or mass network upgrades)
109
+ - `openwisp-radius <https://openwisp.io/docs/stable/user/radius.html>`_:
99
110
  based on FreeRADIUS, allows to implement network access authentication
100
111
  systems like 802.1x WPA2 Enterprise, captive portal authentication,
101
112
  Hotspot 2.0 (802.11u)
102
113
  - `openwisp-network-topology
103
- <https://openwisp.io/docs/dev/network-topology/>`_: provides way to
114
+ <https://openwisp.io/docs/stable/network-topology/>`_: provides way to
104
115
  collect and visualize network topology data from dynamic mesh routing
105
116
  daemons or other network software (e.g.: OpenVPN); it can be used in
106
117
  conjunction with openwisp-monitoring to get a better idea of the state
107
118
  of the network
108
- - `openwisp-ipam <https://openwisp.io/docs/dev/ipam/>`_: allows to manage
109
- the assignment of IP addresses used in the network
110
- - `openwisp-notifications <https://openwisp.io/docs/dev/notifications/>`_:
111
- allows users to be aware of important events happening in the network.
119
+ - `openwisp-ipam <https://openwisp.io/docs/stable/ipam/>`_: allows to
120
+ manage the assignment of IP addresses used in the network
121
+ - `openwisp-notifications
122
+ <https://openwisp.io/docs/stable/notifications/>`_: allows users to be
123
+ aware of important events happening in the network.
112
124
 
113
125
  **For a more complete overview of the OpenWISP modules and architecture**,
114
126
  see the `OpenWISP Architecture Overview
115
- <https://openwisp.io/docs/dev/general/architecture.html>`_.
127
+ <https://openwisp.io/docs/stable/general/architecture.html>`_.
116
128
 
117
129
  .. image:: https://raw.githubusercontent.com/openwisp/openwisp2-docs/master/assets/design/openwisp-logo-black.svg
118
130
  :target: http://openwisp.org
@@ -121,9 +133,9 @@ see the `OpenWISP Architecture Overview
121
133
  Documentation
122
134
  -------------
123
135
 
124
- - `Usage documentation <https://openwisp.io/docs/dev/controller/>`_
136
+ - `Usage documentation <https://openwisp.io/docs/stable/controller/>`_
125
137
  - `Developer documentation
126
- <https://openwisp.io/docs/dev/controller/developer/>`_
138
+ <https://openwisp.io/docs/stable/controller/developer/>`_
127
139
 
128
140
  Contributing
129
141
  ------------
@@ -49,30 +49,31 @@ built on top of its building blocks.
49
49
 
50
50
  Other popular building blocks that are part of the OpenWISP ecosystem are:
51
51
 
52
- - `openwisp-monitoring <https://openwisp.io/docs/dev/monitoring/>`_:
52
+ - `openwisp-monitoring <https://openwisp.io/docs/stable/monitoring/>`_:
53
53
  provides device status monitoring, collection of metrics, charts,
54
54
  alerts, possibility to define custom checks
55
55
  - `openwisp-firmware-upgrader
56
- <https://openwisp.io/docs/dev/firmware-upgrader/>`_: automated firmware
57
- upgrades (single devices or mass network upgrades)
58
- - `openwisp-radius <https://openwisp.io/docs/dev/user/radius.html>`_:
56
+ <https://openwisp.io/docs/stable/firmware-upgrader/>`_: automated
57
+ firmware upgrades (single devices or mass network upgrades)
58
+ - `openwisp-radius <https://openwisp.io/docs/stable/user/radius.html>`_:
59
59
  based on FreeRADIUS, allows to implement network access authentication
60
60
  systems like 802.1x WPA2 Enterprise, captive portal authentication,
61
61
  Hotspot 2.0 (802.11u)
62
62
  - `openwisp-network-topology
63
- <https://openwisp.io/docs/dev/network-topology/>`_: provides way to
63
+ <https://openwisp.io/docs/stable/network-topology/>`_: provides way to
64
64
  collect and visualize network topology data from dynamic mesh routing
65
65
  daemons or other network software (e.g.: OpenVPN); it can be used in
66
66
  conjunction with openwisp-monitoring to get a better idea of the state
67
67
  of the network
68
- - `openwisp-ipam <https://openwisp.io/docs/dev/ipam/>`_: allows to manage
69
- the assignment of IP addresses used in the network
70
- - `openwisp-notifications <https://openwisp.io/docs/dev/notifications/>`_:
71
- allows users to be aware of important events happening in the network.
68
+ - `openwisp-ipam <https://openwisp.io/docs/stable/ipam/>`_: allows to
69
+ manage the assignment of IP addresses used in the network
70
+ - `openwisp-notifications
71
+ <https://openwisp.io/docs/stable/notifications/>`_: allows users to be
72
+ aware of important events happening in the network.
72
73
 
73
74
  **For a more complete overview of the OpenWISP modules and architecture**,
74
75
  see the `OpenWISP Architecture Overview
75
- <https://openwisp.io/docs/dev/general/architecture.html>`_.
76
+ <https://openwisp.io/docs/stable/general/architecture.html>`_.
76
77
 
77
78
  .. image:: https://raw.githubusercontent.com/openwisp/openwisp2-docs/master/assets/design/openwisp-logo-black.svg
78
79
  :target: http://openwisp.org
@@ -81,9 +82,9 @@ see the `OpenWISP Architecture Overview
81
82
  Documentation
82
83
  -------------
83
84
 
84
- - `Usage documentation <https://openwisp.io/docs/dev/controller/>`_
85
+ - `Usage documentation <https://openwisp.io/docs/stable/controller/>`_
85
86
  - `Developer documentation
86
- <https://openwisp.io/docs/dev/controller/developer/>`_
87
+ <https://openwisp.io/docs/stable/controller/developer/>`_
87
88
 
88
89
  Contributing
89
90
  ------------
@@ -1,4 +1,4 @@
1
- VERSION = (1, 1, 0, 'final')
1
+ VERSION = (1, 1, 1, 'final')
2
2
  __version__ = VERSION # alias
3
3
 
4
4
 
@@ -1,11 +1,13 @@
1
1
  import json
2
2
  import logging
3
+ from collections.abc import Iterable
3
4
 
4
5
  import reversion
5
6
  from django import forms
6
7
  from django.conf import settings
7
8
  from django.contrib import admin, messages
8
9
  from django.contrib.admin import helpers
10
+ from django.contrib.admin.actions import delete_selected
9
11
  from django.contrib.admin.models import ADDITION, LogEntry
10
12
  from django.contrib.contenttypes.models import ContentType
11
13
  from django.core.exceptions import (
@@ -566,8 +568,6 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
566
568
  perm = super().has_delete_permission(request)
567
569
  if not obj:
568
570
  return perm
569
- if obj._has_config():
570
- perm = perm and obj.config.is_deactivated()
571
571
  return perm and obj.is_deactivated()
572
572
 
573
573
  def save_form(self, request, form, change):
@@ -765,22 +765,42 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
765
765
  def activate_device(self, request, queryset):
766
766
  self._change_device_status(request, queryset, 'activate')
767
767
 
768
- def get_deleted_objects(self, objs, request, *args, **kwargs):
769
- # Ensure that all selected devices can be deleted, i.e.
770
- # the device should be flagged as deactivated and if it has
771
- # a config object, it's status should be "deactivated".
772
- active_devices = []
773
- for obj in objs:
774
- if not self.has_delete_permission(request, obj):
775
- active_devices.append(obj)
776
- if active_devices:
777
- return (
778
- active_devices,
779
- {self.model._meta.verbose_name_plural: len(active_devices)},
780
- ['active_devices'],
781
- [],
768
+ @admin.action(description=delete_selected.short_description, permissions=['delete'])
769
+ def delete_selected(self, request, queryset):
770
+ response = delete_selected(self, request, queryset)
771
+ if not response:
772
+ return response
773
+ if 'active_devices' in response.context_data.get('perms_lacking', {}):
774
+ active_devices = []
775
+ for device in queryset.iterator():
776
+ if not device.is_deactivated() or (
777
+ device._has_config() and not device.config.is_deactivated()
778
+ ):
779
+ active_devices.append(self._get_device_path(device))
780
+ response.context_data.update(
781
+ {
782
+ 'active_devices': active_devices,
783
+ 'perms_lacking': set(),
784
+ 'title': _('Are you sure?'),
785
+ }
782
786
  )
783
- return super().get_deleted_objects(objs, request, *args, **kwargs)
787
+ return response
788
+
789
+ def get_deleted_objects(self, objs, request, *args, **kwargs):
790
+ to_delete, model_count, perms_needed, protected = super().get_deleted_objects(
791
+ objs, request, *args, **kwargs
792
+ )
793
+ if (
794
+ isinstance(perms_needed, Iterable)
795
+ and len(perms_needed) == 1
796
+ and list(perms_needed)[0] == self.model._meta.verbose_name
797
+ and objs.filter(_is_deactivated=False).exists()
798
+ ):
799
+ if request.POST.get("post"):
800
+ perms_needed = set()
801
+ else:
802
+ perms_needed = {'active_devices'}
803
+ return to_delete, model_count, perms_needed, protected
784
804
 
785
805
  def get_fields(self, request, obj=None):
786
806
  """
@@ -900,6 +920,17 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
900
920
  request._recover_view = True
901
921
  return super().recover_view(request, version_id, extra_context)
902
922
 
923
+ def delete_view(self, request, object_id, extra_context=None):
924
+ extra_context = extra_context or {}
925
+ obj = self.get_object(request, object_id)
926
+ if obj and obj._has_config() and not obj.config.is_deactivated():
927
+ extra_context['deactivating_warning'] = True
928
+ return super().delete_view(request, object_id, extra_context)
929
+
930
+ def delete_model(self, request, obj):
931
+ force_delete = request.POST.get('force_delete') == 'true'
932
+ obj.delete(check_deactivated=not force_delete)
933
+
903
934
  def get_inlines(self, request, obj):
904
935
  inlines = super().get_inlines(request, obj)
905
936
  # this only makes sense in existing devices
@@ -107,6 +107,10 @@ class DeviceDetailView(ProtectedAPIMixin, RetrieveUpdateDestroyAPIView):
107
107
  queryset = Device.objects.select_related('config', 'group', 'organization')
108
108
  permission_classes = ProtectedAPIMixin.permission_classes + (DevicePermission,)
109
109
 
110
+ def perform_destroy(self, instance):
111
+ force_deletion = self.request.query_params.get('force', None) == 'true'
112
+ instance.delete(check_deactivated=(not force_deletion))
113
+
110
114
 
111
115
  class DeviceActivateView(ProtectedAPIMixin, GenericAPIView):
112
116
  serializer_class = serializers.Serializer
@@ -262,6 +262,7 @@ class AbstractDevice(OrgMixin, BaseModel):
262
262
 
263
263
  def clean(self, *args, **kwargs):
264
264
  super().clean(*args, **kwargs)
265
+ self.mac_address = self.mac_address.upper()
265
266
  self._validate_unique_name()
266
267
  self._validate_org_relation('group', field_error='group')
267
268
  self._validate_org_device_limit()
@@ -6,6 +6,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
6
6
  from django.db import models, transaction
7
7
  from django.utils.translation import gettext_lazy as _
8
8
  from jsonfield import JSONField
9
+ from netjsonconfig.exceptions import ValidationError as NetjsonconfigValidationError
9
10
  from swapper import get_model_name
10
11
  from taggit.managers import TaggableManager
11
12
 
@@ -115,7 +116,14 @@ class AbstractTemplate(ShareableOrgMixinUniqueName, BaseConfig):
115
116
  if hasattr(self, 'backend_instance'):
116
117
  del self.backend_instance
117
118
  current = self.__class__.objects.get(pk=self.pk)
118
- update_related_config_status = self.checksum != current.checksum
119
+ try:
120
+ current_checksum = current.checksum
121
+ except NetjsonconfigValidationError:
122
+ # If the Netjsonconfig library upgrade changes the schema,
123
+ # the old configuration may become invalid, raising an exception.
124
+ # Setting the checksum to None forces related configurations to update.
125
+ current_checksum = None
126
+ update_related_config_status = self.checksum != current_checksum
119
127
  # save current changes
120
128
  super().save(*args, **kwargs)
121
129
  # update relations
@@ -0,0 +1,11 @@
1
+ #deactivating-warning .warning p {
2
+ margin-top: 0px;
3
+ }
4
+ #main ul.messagelist li.warning ul li {
5
+ display: list-item;
6
+ padding: 0px;
7
+ background: inherit;
8
+ }
9
+ ul.messagelist li {
10
+ font-size: unset;
11
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ (function ($) {
4
+ $(document).ready(function () {
5
+ $("#warning-ack").click(function (event) {
6
+ event.preventDefault();
7
+ $("#deactivating-warning").slideUp("fast");
8
+ $("#delete-confirm-container").slideDown("fast");
9
+ $('input[name="force_delete"]').val("true");
10
+ });
11
+ });
12
+ })(django.jQuery);
@@ -0,0 +1,81 @@
1
+ {% extends "admin/delete_confirmation.html" %}
2
+ {% load i18n static %}
3
+
4
+ {% block extrastyle %}
5
+ {{ block.super }}
6
+ <link rel="stylesheet" type="text/css" href="{% static 'config/css/device-delete-confirmation.css' %}" />
7
+ {% endblock extrastyle %}
8
+
9
+ {% block content %}
10
+ {% if perms_lacking %}
11
+ <p>{% blocktranslate with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktranslate %}</p>
12
+ <ul>
13
+ {% for obj in perms_lacking %}
14
+ <li>{{ obj }}</li>
15
+ {% endfor %}
16
+ </ul>
17
+ {% elif protected %}
18
+ <p>{% blocktranslate with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would require deleting the following protected related objects:{% endblocktranslate %}</p>
19
+ <ul>
20
+ {% for obj in protected %}
21
+ <li>{{ obj }}</li>
22
+ {% endfor %}
23
+ </ul>
24
+ {% else %}
25
+ {% if deactivating_warning %}
26
+ <div id="deactivating-warning">
27
+ <ul class="messagelist">
28
+ <li class="warning">
29
+ <p>
30
+ <strong>
31
+ {% translate 'Warning: Device is not fully deactivated.' %}
32
+ </strong>
33
+ </p>
34
+ <p>
35
+ {% blocktranslate %}
36
+ This device is still in the process of being deactivated,
37
+ meaning its configuration is still present on the device.
38
+ {% endblocktranslate %}
39
+ </p>
40
+ <p>
41
+ {% blocktranslate %}
42
+ To ensure its configuration is removed, please
43
+ wait until its status changes to
44
+ <strong>"deactivated"</strong>.<br>
45
+ If you proceed now, the device will be deleted,
46
+ but its configuration will remain active.
47
+ {% endblocktranslate %}
48
+ </p>
49
+ <form>
50
+ <input type="submit" class="button danger-btn" id="warning-ack"
51
+ value="{% translate 'I understand the risks, delete the device' %}">
52
+ <a class="button cancel-link">{% translate 'No, take me back' %}</a>
53
+ </form>
54
+ </li>
55
+ </ul>
56
+ </div>
57
+ {% endif %}
58
+ <div id="delete-confirm-container" {% if deactivating_warning %}style="display:none;"{% endif %}>
59
+ <p>{% blocktranslate with escaped_object=object %}Are you sure you want to delete the {{ object_name }}
60
+ "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktranslate %}</p>
61
+ {% include "admin/includes/object_delete_summary.html" %}
62
+ <h2>{% translate "Objects" %}</h2>
63
+ <ul id="deleted-objects">{{ deleted_objects|unordered_list }}</ul>
64
+ <form method="post">{% csrf_token %}
65
+ <div>
66
+ <input type="hidden" name="post" value="yes">
67
+ {% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
68
+ {% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
69
+ <input type="submit" value="{% translate 'Yes, I’m sure' %}">
70
+ {% if deactivating_warning %}<input type="hidden" name="force_delete" value="false">{% endif %}
71
+ <a href="#" class="button cancel-link">{% translate "No, take me back" %}</a>
72
+ </div>
73
+ </form>
74
+ </div>
75
+ {% endif %}
76
+ {% endblock %}
77
+
78
+ {% block footer %}
79
+ {{ block.super }}
80
+ <script type="text/javascript" src="{% static 'config/js/device-delete-confirmation.js' %}"></script>
81
+ {% endblock %}
@@ -0,0 +1,87 @@
1
+ {% extends "admin/delete_selected_confirmation.html" %}
2
+ {% load i18n l10n admin_urls static %}
3
+
4
+ {% block extrastyle %}
5
+ {{ block.super }}
6
+ <link rel="stylesheet" type="text/css" href="{% static 'config/css/device-delete-confirmation.css' %}" />
7
+ {% endblock extrastyle %}
8
+
9
+ {% block content %}
10
+ {% if perms_lacking %}
11
+ <p>{% blocktranslate %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktranslate %}</p>
12
+ <ul>{{ perms_lacking|unordered_list }}</ul>
13
+ {% elif protected %}
14
+ <p>{% blocktranslate %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktranslate %}</p>
15
+ <ul>{{ protected|unordered_list }}</ul>
16
+ {% else %}
17
+ {% if active_devices %}
18
+ <div id="deactivating-warning">
19
+ <ul class="messagelist">
20
+ <li class="warning">
21
+ <p>
22
+ <strong>
23
+ {% blocktranslate count counter=active_devices|length %}
24
+ Warning: Device is not fully deactivated.
25
+ {% plural %}
26
+ Warning: Some devices are not fully deactivated.
27
+ {% endblocktranslate %}
28
+ </strong>
29
+ </p>
30
+ <p>
31
+ {% blocktranslate count counter=active_devices|length %}
32
+ The device below is either still active or
33
+ in the process of being deactivated:
34
+ {% plural %}
35
+ The devices listed below are either still active
36
+ or in the process of being deactivated:
37
+ {% endblocktranslate %}
38
+ </p>
39
+ <ul>{{ active_devices|unordered_list }}</ul>
40
+ <p>
41
+ {% blocktranslate count counter=active_devices|length %}
42
+ To ensure its configuration is removed, please
43
+ wait until its status changes to <strong>"deactivated"</strong>.<br>
44
+ If you proceed now, the device will be deleted,
45
+ but its configuration will remain active.
46
+ {% plural %}
47
+ To ensure their configurations are removed, please
48
+ wait until their status changes to <strong>"deactivated"</strong>.<br>
49
+ If you proceed now, the devices will be deleted,
50
+ but their configurations will remain active.
51
+ {% endblocktranslate %}
52
+ </p>
53
+ <form>
54
+ <input type="submit" class="button danger-btn" id="warning-ack"
55
+ value="{% blocktranslate count counter=active_devices|length %}I understand the risks, delete the device{% plural %}I understand the risks, delete the devices{% endblocktranslate %}">
56
+ <a class="button cancel-link">{% translate 'No, take me back' %}</a>
57
+ </form>
58
+ </li>
59
+ </ul>
60
+ </div>
61
+ {% endif %}
62
+ <div id="delete-confirm-container" {% if active_devices %}style="display:none;"{% endif %}>
63
+ <p>{% blocktranslate %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktranslate %}</p>
64
+ {% include "admin/includes/object_delete_summary.html" %}
65
+ <h2>{% translate "Objects" %}</h2>
66
+ {% for deletable_object in deletable_objects %}
67
+ <ul>{{ deletable_object|unordered_list }}</ul>
68
+ {% endfor %}
69
+ <form method="post">{% csrf_token %}
70
+ <div>
71
+ {% for obj in queryset %}
72
+ <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}">
73
+ {% endfor %}
74
+ <input type="hidden" name="action" value="delete_selected">
75
+ <input type="hidden" name="post" value="yes">
76
+ <input type="submit" value="{% translate 'Yes, I’m sure' %}">
77
+ <a href="#" class="button cancel-link">{% translate "No, take me back" %}</a>
78
+ </div>
79
+ </form>
80
+ </div>
81
+ {% endif %}
82
+ {% endblock %}
83
+
84
+ {% block footer %}
85
+ {{ block.super }}
86
+ <script type="text/javascript" src="{% static 'config/js/device-delete-confirmation.js' %}"></script>
87
+ {% endblock %}
@@ -2120,9 +2120,8 @@ class TestTransactionAdmin(
2120
2120
  )
2121
2121
  # Save buttons are absent on deactivated device
2122
2122
  self.assertNotContains(response, self._save_btn_html)
2123
- # Delete button is not present if config status is deactivating
2124
2123
  self.assertEqual(device.config.status, 'deactivating')
2125
- self.assertNotContains(response, delete_btn_html)
2124
+ self.assertContains(response, delete_btn_html)
2126
2125
  self.assertNotContains(response, self._deactivate_btn_html)
2127
2126
  self.assertContains(response, self._activate_btn_html)
2128
2127
  # Verify adding a new DeviceLocation and DeviceConnection is not allowed
@@ -539,6 +539,22 @@ class TestConfigApi(
539
539
  self.assertEqual(response.status_code, 204)
540
540
  self.assertEqual(Device.objects.count(), 0)
541
541
 
542
+ def test_deactivating_device_force_deletion(self):
543
+ self._create_template(required=True)
544
+ device = self._create_device()
545
+ config = self._create_config(device=device)
546
+ device.deactivate()
547
+ path = reverse('config_api:device_detail', args=[device.pk])
548
+
549
+ with self.subTest(
550
+ 'Test force deleting device with config in deactivating state'
551
+ ):
552
+ self.assertEqual(device.is_deactivated(), True)
553
+ self.assertEqual(config.is_deactivating(), True)
554
+ response = self.client.delete(f'{path}?force=true')
555
+ self.assertEqual(response.status_code, 204)
556
+ self.assertEqual(Device.objects.count(), 0)
557
+
542
558
  def test_template_create_no_org_api(self):
543
559
  self.assertEqual(Template.objects.count(), 0)
544
560
  path = reverse('config_api:template_list')
@@ -69,6 +69,14 @@ class TestDevice(
69
69
  else:
70
70
  self.fail('ValidationError not raised for "{0}"'.format(mac_address))
71
71
 
72
+ def test_device_mac_address_uppercase(self):
73
+ device = self._create_device(mac_address='00:1a:2b:3c:4d:5e')
74
+ device.full_clean()
75
+ self.assertEqual(device.mac_address, '00:1A:2B:3C:4D:5E')
76
+ device.save()
77
+ device.refresh_from_db()
78
+ self.assertEqual(device.mac_address, '00:1A:2B:3C:4D:5E')
79
+
72
80
  def test_config_status_modified(self):
73
81
  c = self._create_config(device=self._create_device(), status='applied')
74
82
  self.assertEqual(c.status, 'applied')