openwisp-controller 1.2.2__tar.gz → 1.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.
Files changed (389) hide show
  1. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/CHANGES.rst +120 -0
  2. {openwisp_controller-1.2.2/openwisp_controller.egg-info → openwisp_controller-1.3}/PKG-INFO +15 -14
  3. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/__init__.py +1 -1
  4. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/admin.py +143 -31
  5. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/serializers.py +30 -1
  6. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/views.py +17 -19
  7. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/apps.py +128 -78
  8. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/base.py +3 -4
  9. openwisp_controller-1.3/openwisp_controller/config/base/cache.py +402 -0
  10. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/channels_consumer.py +1 -7
  11. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/config.py +153 -52
  12. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/device.py +77 -9
  13. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/device_group.py +5 -21
  14. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/multitenancy.py +21 -16
  15. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/template.py +4 -3
  16. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/vpn.py +113 -9
  17. openwisp_controller-1.3/openwisp_controller/config/base/whois.py +210 -0
  18. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/controller/views.py +53 -14
  19. openwisp_controller-1.3/openwisp_controller/config/converters.py +27 -0
  20. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/handlers.py +64 -15
  21. openwisp_controller-1.3/openwisp_controller/config/management/commands/clear_last_ip.py +26 -0
  22. openwisp_controller-1.3/openwisp_controller/config/management/commands/print_cache_dependencies.py +22 -0
  23. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py +3 -11
  24. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0018_config_context.py +2 -6
  25. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0023_update_context.py +2 -6
  26. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0028_template_default_values.py +2 -6
  27. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0036_device_group.py +1 -5
  28. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0042_multiple_wireguard_tunnels.py +9 -4
  29. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0048_wifi_radio_band_migration.py +13 -6
  30. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0049_devicegroup_context.py +2 -6
  31. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0051_organizationconfigsettings_context.py +2 -6
  32. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0057_populate_vpnclient_template.py +8 -2
  33. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0059_zerotier_templates_ow_zt_to_global.py +10 -5
  34. openwisp_controller-1.3/openwisp_controller/config/migrations/0061_config_checksum_db.py +330 -0
  35. openwisp_controller-1.3/openwisp_controller/config/migrations/0062_whoisinfo.py +89 -0
  36. openwisp_controller-1.3/openwisp_controller/config/migrations/0063_replace_jsonfield_with_django_builtin.py +122 -0
  37. openwisp_controller-1.3/openwisp_controller/config/migrations/0064_template_notes.py +18 -0
  38. openwisp_controller-1.3/openwisp_controller/config/migrations/0065_whoisinfo_unreferenced_since.py +19 -0
  39. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/__init__.py +13 -1
  40. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/models.py +11 -0
  41. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/settings.py +20 -5
  42. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/signals.py +8 -0
  43. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/css/admin.css +48 -31
  44. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/css/lib/advanced-mode.css +70 -62
  45. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/css/lib/jsonschema-ui.css +18 -18
  46. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/lib/advanced-mode.js +1 -1
  47. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/relevant_templates.js +1 -1
  48. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/tabs.js +5 -0
  49. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/widget.js +3 -3
  50. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/import_export/import-openwisp.css +7 -7
  51. openwisp_controller-1.3/openwisp_controller/config/static/whois/css/whois.css +110 -0
  52. openwisp_controller-1.3/openwisp_controller/config/static/whois/images/whois_globe.svg +3 -0
  53. openwisp_controller-1.3/openwisp_controller/config/static/whois/js/whois.js +62 -0
  54. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tasks.py +32 -22
  55. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tasks_zerotier.py +10 -58
  56. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/change_form.html +8 -0
  57. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/device/change_form.html +1 -1
  58. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/pytest.py +3 -3
  59. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_admin.py +284 -93
  60. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_api.py +112 -22
  61. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_config.py +619 -2
  62. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_controller.py +500 -316
  63. openwisp_controller-1.3/openwisp_controller/config/tests/test_converters.py +23 -0
  64. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_device.py +118 -67
  65. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_handlers.py +1 -1
  66. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_notifications.py +3 -1
  67. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_selenium.py +182 -40
  68. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_views.py +1 -1
  69. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_vpn.py +206 -29
  70. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/utils.py +43 -0
  71. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/utils.py +84 -21
  72. openwisp_controller-1.3/openwisp_controller/config/whois/commands.py +68 -0
  73. openwisp_controller-1.3/openwisp_controller/config/whois/handlers.py +45 -0
  74. openwisp_controller-1.3/openwisp_controller/config/whois/mixins.py +20 -0
  75. openwisp_controller-1.3/openwisp_controller/config/whois/serializers.py +17 -0
  76. openwisp_controller-1.3/openwisp_controller/config/whois/service.py +290 -0
  77. openwisp_controller-1.3/openwisp_controller/config/whois/tasks.py +138 -0
  78. openwisp_controller-1.3/openwisp_controller/config/whois/tests/tests.py +1353 -0
  79. openwisp_controller-1.3/openwisp_controller/config/whois/tests/utils.py +164 -0
  80. openwisp_controller-1.3/openwisp_controller/config/whois/utils.py +74 -0
  81. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/admin.py +26 -2
  82. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/api/urls.py +1 -1
  83. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/api/views.py +15 -13
  84. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/apps.py +6 -3
  85. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/base/models.py +85 -35
  86. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/channels/routing.py +3 -3
  87. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/commands.py +1 -1
  88. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/connectors/openwrt/ssh.py +1 -1
  89. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/connectors/ssh.py +3 -3
  90. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0001_initial.py +6 -10
  91. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0007_command.py +1 -5
  92. openwisp_controller-1.3/openwisp_controller/connection/migrations/0010_replace_jsonfield_with_django_builtin.py +47 -0
  93. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/static/connection/css/command-inline.css +24 -10
  94. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/static/connection/js/commands.js +2 -1
  95. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tasks.py +16 -8
  96. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/pytest.py +5 -4
  97. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_admin.py +37 -17
  98. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_api.py +4 -4
  99. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_models.py +154 -19
  100. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_notifications.py +6 -4
  101. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_selenium.py +3 -7
  102. openwisp_controller-1.3/openwisp_controller/connection/tests/test_tasks.py +317 -0
  103. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/utils.py +1 -1
  104. openwisp_controller-1.3/openwisp_controller/geo/admin.py +249 -0
  105. openwisp_controller-1.3/openwisp_controller/geo/api/filters.py +58 -0
  106. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/api/serializers.py +74 -6
  107. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/api/views.py +129 -13
  108. openwisp_controller-1.3/openwisp_controller/geo/apps.py +177 -0
  109. openwisp_controller-1.3/openwisp_controller/geo/base/models.py +219 -0
  110. openwisp_controller-1.3/openwisp_controller/geo/channels/consumers.py +42 -0
  111. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/channels/routing.py +11 -3
  112. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/handlers.py +83 -0
  113. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/mixins.py +37 -0
  114. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/service.py +253 -0
  115. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/tasks.py +47 -0
  116. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/tests/tests.py +1415 -0
  117. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/tests/utils.py +43 -0
  118. openwisp_controller-1.3/openwisp_controller/geo/estimated_location/utils.py +143 -0
  119. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/migrations/0001_initial.py +4 -1
  120. openwisp_controller-1.3/openwisp_controller/geo/migrations/0004_location_is_estimated.py +22 -0
  121. openwisp_controller-1.3/openwisp_controller/geo/migrations/0005_organizationgeosettings.py +55 -0
  122. openwisp_controller-1.3/openwisp_controller/geo/migrations/0006_create_geo_settings_for_existing_orgs.py +40 -0
  123. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/migrations/__init__.py +15 -2
  124. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/models.py +12 -1
  125. openwisp_controller-1.3/openwisp_controller/geo/settings.py +13 -0
  126. openwisp_controller-1.3/openwisp_controller/geo/tasks.py +3 -0
  127. openwisp_controller-1.3/openwisp_controller/geo/templates/admin/geo/device_location_inline.html +11 -0
  128. openwisp_controller-1.3/openwisp_controller/geo/templates/admin/geo/location/change_form.html +11 -0
  129. openwisp_controller-1.3/openwisp_controller/geo/tests/pytest.py +201 -0
  130. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/test_admin.py +74 -0
  131. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/test_admin_inline.py +11 -15
  132. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/test_api.py +638 -22
  133. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/test_selenium.py +47 -4
  134. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/utils.py +6 -3
  135. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/utils.py +10 -0
  136. openwisp_controller-1.3/openwisp_controller/pki/__init__.py +0 -0
  137. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/api/views.py +5 -9
  138. openwisp_controller-1.3/openwisp_controller/pki/base/__init__.py +0 -0
  139. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0001_initial.py +2 -8
  140. openwisp_controller-1.3/openwisp_controller/pki/migrations/0012_alter_ca_extensions_alter_ca_key_length_and_more.py +70 -0
  141. openwisp_controller-1.3/openwisp_controller/pki/tests/__init__.py +0 -0
  142. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/tests/test_api.py +64 -51
  143. openwisp_controller-1.3/openwisp_controller/settings.py +8 -0
  144. openwisp_controller-1.3/openwisp_controller/subnet_division/__init__.py +0 -0
  145. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/admin.py +1 -1
  146. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/base/models.py +1 -1
  147. openwisp_controller-1.3/openwisp_controller/subnet_division/rule_types/__init__.py +0 -0
  148. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/rule_types/base.py +64 -24
  149. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/rule_types/vpn.py +7 -3
  150. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/static/subnet-division/css/subnet-division.css +3 -3
  151. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/tasks.py +9 -0
  152. openwisp_controller-1.3/openwisp_controller/subnet_division/tests/__init__.py +0 -0
  153. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/tests/test_admin.py +137 -3
  154. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/tests/test_models.py +103 -12
  155. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/tests/mixins.py +5 -0
  156. openwisp_controller-1.3/openwisp_controller/tests/test_migrationgraph.py +32 -0
  157. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/tests/test_selenium.py +5 -3
  158. {openwisp_controller-1.2.2 → openwisp_controller-1.3/openwisp_controller.egg-info}/PKG-INFO +15 -14
  159. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller.egg-info/SOURCES.txt +45 -0
  160. openwisp_controller-1.3/openwisp_controller.egg-info/requires.txt +19 -0
  161. openwisp_controller-1.3/requirements.txt +19 -0
  162. openwisp_controller-1.2.2/openwisp_controller/config/migrations/0061_config_checksum_db.py +0 -57
  163. openwisp_controller-1.2.2/openwisp_controller/connection/tests/test_tasks.py +0 -87
  164. openwisp_controller-1.2.2/openwisp_controller/geo/admin.py +0 -142
  165. openwisp_controller-1.2.2/openwisp_controller/geo/api/filters.py +0 -27
  166. openwisp_controller-1.2.2/openwisp_controller/geo/apps.py +0 -119
  167. openwisp_controller-1.2.2/openwisp_controller/geo/base/models.py +0 -65
  168. openwisp_controller-1.2.2/openwisp_controller/geo/channels/consumers.py +0 -19
  169. openwisp_controller-1.2.2/openwisp_controller/geo/tests/pytest.py +0 -110
  170. openwisp_controller-1.2.2/openwisp_controller/settings.py +0 -3
  171. openwisp_controller-1.2.2/openwisp_controller.egg-info/requires.txt +0 -18
  172. openwisp_controller-1.2.2/requirements.txt +0 -18
  173. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/LICENSE +0 -0
  174. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/MANIFEST.in +0 -0
  175. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/README.rst +0 -0
  176. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/admin.py +0 -0
  177. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/base.py +0 -0
  178. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/checks.py +0 -0
  179. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/__init__.py +0 -0
  180. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/__init__.py +0 -0
  181. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/download_views.py +0 -0
  182. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/filters.py +0 -0
  183. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/urls.py +0 -0
  184. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/api/zerotier_service.py +0 -0
  185. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/__init__.py +0 -0
  186. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/base/tag.py +0 -0
  187. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/controller/__init__.py +0 -0
  188. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/controller/urls.py +0 -0
  189. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/crypto.py +0 -0
  190. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/exceptions.py +0 -0
  191. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/exportable.py +0 -0
  192. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/filters.py +0 -0
  193. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/fixtures/test_templates.json +0 -0
  194. {openwisp_controller-1.2.2/openwisp_controller/config/sortedm2m → openwisp_controller-1.3/openwisp_controller/config/management}/__init__.py +0 -0
  195. {openwisp_controller-1.2.2/openwisp_controller/connection → openwisp_controller-1.3/openwisp_controller/config/management/commands}/__init__.py +0 -0
  196. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0003_template_tags.py +0 -0
  197. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0004_add_device_model.py +0 -0
  198. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0005_populate_device.py +0 -0
  199. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0006_config_device_not_null.py +0 -0
  200. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0007_simplify_config.py +0 -0
  201. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0008_update_indexes.py +0 -0
  202. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0009_device_system.py +0 -0
  203. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0010_auto_20180106_1814.py +0 -0
  204. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0011_update_device_mac_address.py +0 -0
  205. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0012_auto_20180219_1501.py +0 -0
  206. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0013_last_ip_management_ip_and_status_applied.py +0 -0
  207. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0014_device_hardware_id.py +0 -0
  208. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0015_default_groups_permissions.py +0 -0
  209. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0016_default_organization_config_settings.py +0 -0
  210. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0017_template_name_organization_unique_together.py +0 -0
  211. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0019_organization_mac_add_hardware_id_name_unique_together.py +0 -0
  212. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0020_remove_config_organization.py +0 -0
  213. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0021_vpn_key.py +0 -0
  214. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0022_vpn_format_dh.py +0 -0
  215. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0024_update_context_data.py +0 -0
  216. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0025_update_device_key.py +0 -0
  217. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0026_hardware_id_not_unique.py +0 -0
  218. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0027_add_indexes_on_ip_fields.py +0 -0
  219. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0029_merge_django_netjsonconfig.py +0 -0
  220. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0030_django_taggit_update.py +0 -0
  221. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0031_update_vpn_dh_param.py +0 -0
  222. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0032_update_legacy_vpn_backend.py +0 -0
  223. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0033_name_unique_per_organization.py +0 -0
  224. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0034_template_required.py +0 -0
  225. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0035_device_name_unique_optional.py +0 -0
  226. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0037_alter_taggedtemplate.py +0 -0
  227. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0038_vpn_subnet.py +0 -0
  228. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0039_wireguard_vxlan_ipam.py +0 -0
  229. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0040_vpnclient_ip_setnull.py +0 -0
  230. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0041_default_groups_organizationconfigsettings_permission.py +0 -0
  231. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0043_devicegroup_templates.py +0 -0
  232. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0044_config_error_reason.py +0 -0
  233. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0045_alter_vpn_webhook_endpoint.py +0 -0
  234. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0046_organizationlimits.py +0 -0
  235. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0047_add_organizationlimits.py +0 -0
  236. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0050_alter_vpnclient_unique_together.py +0 -0
  237. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0052_vpn_node_network_id.py +0 -0
  238. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0053_vpnclient_secret.py +0 -0
  239. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0054_device__is_deactivated.py +0 -0
  240. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0055_alter_config_status.py +0 -0
  241. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0056_vpnclient_template.py +0 -0
  242. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0058_alter_vpnclient_template.py +0 -0
  243. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/migrations/0060_cleanup_api_task_notification_types.py +0 -0
  244. {openwisp_controller-1.2.2/openwisp_controller/connection/base → openwisp_controller-1.3/openwisp_controller/config/sortedm2m}/__init__.py +0 -0
  245. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/sortedm2m/fields.py +0 -0
  246. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/sortedm2m/forms.py +0 -0
  247. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/css/device-delete-confirmation.css +0 -0
  248. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/css/devicegroup.css +0 -0
  249. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/css/lib/img/jsoneditor-icons.svg +0 -0
  250. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/device-delete-confirmation.js +0 -0
  251. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/lib/jsonschema-ui.js +0 -0
  252. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/lib/tomorrow_night_bright.js +0 -0
  253. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/management_ip.js +0 -0
  254. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/preview.js +0 -0
  255. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/switcher.js +0 -0
  256. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/unsaved_changes.js +0 -0
  257. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/utils.js +0 -0
  258. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/config/js/vpn.js +0 -0
  259. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/sortedm2m/patch_sortedm2m.js +0 -0
  260. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/static/support.css +0 -0
  261. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/change_device_group.html +0 -0
  262. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/change_list_device.html +0 -0
  263. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/clone_template_form.html +0 -0
  264. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/device/delete_confirmation.html +0 -0
  265. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +0 -0
  266. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/device_recover_form.html +0 -0
  267. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/jsonschema-widget.html +0 -0
  268. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/preview.html +0 -0
  269. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/config/system_context.html +0 -0
  270. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/device_group/change_form.html +0 -0
  271. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/admin/import_export/import.html +0 -0
  272. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/templates/reversion/config/revision_form.html +0 -0
  273. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/__init__.py +0 -0
  274. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_apps.py +0 -0
  275. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_device_group.py +0 -0
  276. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_tag.py +0 -0
  277. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/tests/test_template.py +0 -0
  278. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/urls.py +0 -0
  279. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/validators.py +0 -0
  280. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/views.py +0 -0
  281. {openwisp_controller-1.2.2/openwisp_controller/connection/connectors → openwisp_controller-1.3/openwisp_controller/config/whois}/__init__.py +0 -0
  282. {openwisp_controller-1.2.2/openwisp_controller/connection/connectors/openwrt → openwisp_controller-1.3/openwisp_controller/config/whois/tests}/__init__.py +0 -0
  283. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/config/widgets.py +0 -0
  284. {openwisp_controller-1.2.2/openwisp_controller/connection/tests → openwisp_controller-1.3/openwisp_controller/connection}/__init__.py +0 -0
  285. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/api/serializers.py +0 -0
  286. {openwisp_controller-1.2.2/openwisp_controller/geo → openwisp_controller-1.3/openwisp_controller/connection/base}/__init__.py +0 -0
  287. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/channels/consumers.py +0 -0
  288. {openwisp_controller-1.2.2/openwisp_controller/geo/api → openwisp_controller-1.3/openwisp_controller/connection/connectors}/__init__.py +0 -0
  289. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/connectors/airos/snmp.py +0 -0
  290. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/connectors/exceptions.py +0 -0
  291. {openwisp_controller-1.2.2/openwisp_controller/geo/base → openwisp_controller-1.3/openwisp_controller/connection/connectors/openwrt}/__init__.py +0 -0
  292. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/connectors/openwrt/snmp.py +0 -0
  293. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/connectors/snmp.py +0 -0
  294. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/exceptions.py +0 -0
  295. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0002_credentials_auto_add.py +0 -0
  296. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0003_default_group_permissions.py +0 -0
  297. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0004_django3_1_upgrade.py +0 -0
  298. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0005_device_connection_failure_reason.py +0 -0
  299. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0006_name_unique_per_organization.py +0 -0
  300. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0008_remove_conflicting_deviceconnections.py +0 -0
  301. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/0009_alter_deviceconnection_unique_together.py +0 -0
  302. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/migrations/__init__.py +0 -0
  303. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/models.py +0 -0
  304. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/schema.py +0 -0
  305. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/settings.py +0 -0
  306. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/signals.py +0 -0
  307. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/static/connection/css/credentials.css +0 -0
  308. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/static/connection/js/credentials.js +0 -0
  309. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/static/connection/js/lib/reconnecting-websocket.min.js +0 -0
  310. {openwisp_controller-1.2.2/openwisp_controller/geo/channels → openwisp_controller-1.3/openwisp_controller/connection/tests}/__init__.py +0 -0
  311. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/base.py +0 -0
  312. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test-key.ed25519 +0 -0
  313. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test-key.rsa +0 -0
  314. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_command_utilities.py +0 -0
  315. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_snmp.py +0 -0
  316. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/tests/test_ssh.py +0 -0
  317. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/utils.py +0 -0
  318. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/connection/widgets.py +0 -0
  319. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/context_processors.py +0 -0
  320. {openwisp_controller-1.2.2/openwisp_controller/geo/tests → openwisp_controller-1.3/openwisp_controller/geo}/__init__.py +0 -0
  321. {openwisp_controller-1.2.2/openwisp_controller/pki → openwisp_controller-1.3/openwisp_controller/geo/api}/__init__.py +0 -0
  322. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/api/urls.py +0 -0
  323. {openwisp_controller-1.2.2/openwisp_controller/pki → openwisp_controller-1.3/openwisp_controller/geo}/base/__init__.py +0 -0
  324. {openwisp_controller-1.2.2/openwisp_controller/pki/tests → openwisp_controller-1.3/openwisp_controller/geo/channels}/__init__.py +0 -0
  325. {openwisp_controller-1.2.2/openwisp_controller/subnet_division → openwisp_controller-1.3/openwisp_controller/geo/estimated_location}/__init__.py +0 -0
  326. {openwisp_controller-1.2.2/openwisp_controller/subnet_division/rule_types → openwisp_controller-1.3/openwisp_controller/geo/estimated_location/tests}/__init__.py +0 -0
  327. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/exportable.py +0 -0
  328. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/migrations/0002_default_groups_permissions.py +0 -0
  329. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/migrations/0003_alter_devicelocation_floorplan_location.py +0 -0
  330. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/templates/admin/widgets/foreign_key_raw_id.html +0 -0
  331. {openwisp_controller-1.2.2/openwisp_controller/subnet_division → openwisp_controller-1.3/openwisp_controller/geo}/tests/__init__.py +0 -0
  332. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/test_apps.py +0 -0
  333. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/geo/tests/test_models.py +0 -0
  334. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/migrations.py +0 -0
  335. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/mixins.py +0 -0
  336. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/admin.py +0 -0
  337. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/api/serializers.py +0 -0
  338. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/api/urls.py +0 -0
  339. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/apps.py +0 -0
  340. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/base/models.py +0 -0
  341. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0002_add_organization_name.py +0 -0
  342. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0003_fill_organization_name.py +0 -0
  343. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0004_auto_20180106_1814.py +0 -0
  344. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0005_organizational_unit_name.py +0 -0
  345. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0006_add_x509_passphrase_field.py +0 -0
  346. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0007_default_groups_permissions.py +0 -0
  347. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0008_serial_number_length.py +0 -0
  348. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0009_common_name_maxlength_64.py +0 -0
  349. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0010_common_name_organization_unique.py +0 -0
  350. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/0011_disallowed_blank_key_length_or_digest.py +0 -0
  351. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/migrations/__init__.py +0 -0
  352. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/models.py +0 -0
  353. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/static/admin/pki/js/show-org-field.js +0 -0
  354. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/templates/admin/pki/change_form.html +0 -0
  355. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/tests/test_admin.py +0 -0
  356. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/tests/test_models.py +0 -0
  357. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/tests/utils.py +0 -0
  358. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/urls.py +0 -0
  359. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/pki/utils.py +0 -0
  360. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/routing.py +0 -0
  361. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/serializers.py +0 -0
  362. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/apps.py +0 -0
  363. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/filters.py +0 -0
  364. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/migrations/0001_initial.py +0 -0
  365. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/migrations/0002_default_group_migration.py +0 -0
  366. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/migrations/0003_related_field_allow_blank.py +0 -0
  367. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/migrations/0004_index_rule_on_delete.py +0 -0
  368. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/migrations/0005_number_of_subnets_and_ips.py +0 -0
  369. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/migrations/__init__.py +0 -0
  370. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/models.py +0 -0
  371. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/rule_types/device.py +0 -0
  372. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/settings.py +0 -0
  373. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/signals.py +0 -0
  374. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/static/subnet-division/js/subnet-division.js +0 -0
  375. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/tests/helpers.py +0 -0
  376. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/tests/test_rule.py +0 -0
  377. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/subnet_division/utils.py +0 -0
  378. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/tests/__init__.py +0 -0
  379. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/tests/test_users_integration.py +0 -0
  380. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/tests/test_utilities.py +0 -0
  381. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/tests/utils.py +0 -0
  382. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/urls.py +0 -0
  383. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller/vpn_backends.py +0 -0
  384. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller.egg-info/dependency_links.txt +0 -0
  385. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller.egg-info/not-zip-safe +0 -0
  386. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/openwisp_controller.egg-info/top_level.txt +0 -0
  387. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/pyproject.toml +0 -0
  388. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/setup.cfg +0 -0
  389. {openwisp_controller-1.2.2 → openwisp_controller-1.3}/setup.py +0 -0
@@ -1,6 +1,126 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ Version 1.3.0 [2026-09-03]
5
+ --------------------------
6
+
7
+ Features
8
+ ~~~~~~~~
9
+
10
+ - Added `WHOIS lookup and geographic location estimation
11
+ <https://github.com/openwisp/openwisp-controller/issues/1034>`_ for
12
+ devices, including WHOIS details in the admin UI, automatic refresh of
13
+ stale WHOIS data, estimated-location notifications and warnings, and
14
+ admin and API filters for estimated locations.
15
+ - Added `WebSocket support for broadcasting all location updates
16
+ <https://github.com/openwisp/openwisp-controller/issues/1157>`_.
17
+ - Added an `API endpoint for retrieving indoor device coordinates
18
+ <https://github.com/openwisp/openwisp-controller/issues/828>`_ for a
19
+ location.
20
+ - Added `location and floorplan filters to the device list API
21
+ <https://github.com/openwisp/openwisp-controller/issues/1158>`_,
22
+ allowing searches by location name and filtering by location or
23
+ floorplan UUID.
24
+ - Added `generic cache invalidation for configuration models
25
+ <https://github.com/openwisp/openwisp-controller/issues/1095>`_. Models
26
+ can declare cache dependencies using ``CacheDependency``, and the new
27
+ ``print_cache_dependencies`` management command can be used to inspect
28
+ the dependency graph.
29
+ - Added `notes to configuration templates
30
+ <https://github.com/openwisp/openwisp-controller/issues/1412>`_.
31
+ - Allowed devices to update their name during re-registration.
32
+
33
+ Changes
34
+ ~~~~~~~
35
+
36
+ Other changes
37
+ +++++++++++++
38
+
39
+ - Enforced the `deactivated device state across OpenWISP Controller
40
+ <https://github.com/openwisp/openwisp-controller/issues/1338>`_.
41
+ Deactivated devices can no longer register, receive configuration
42
+ updates, run remote commands, or receive automatically assigned
43
+ templates. Cleanup and security operations continue to work normally.
44
+ - Added `VPN subnet and IP address details to the REST API
45
+ <https://github.com/openwisp/openwisp-controller/issues/1003>`_.
46
+ - Allowed deleting organizations containing active devices while
47
+ displaying a warning about the active devices that will be affected.
48
+ - Replaced the third-party ``jsonfield`` implementation with Django's
49
+ built-in ``JSONField`` `#1061
50
+ <https://github.com/openwisp/openwisp-controller/issues/1061>`_,
51
+ including migration handling for databases upgraded from older OpenWISP
52
+ Controller versions.
53
+ - Changed VPN synchronization errors and recovery events to use
54
+ ``generic_message`` notifications `#1049
55
+ <https://github.com/openwisp/openwisp-controller/issues/1049>`_.
56
+ - Improved command status visibility in the admin interface by
57
+ highlighting statuses with different colors `#812
58
+ <https://github.com/openwisp/openwisp-controller/issues/812>`_.
59
+
60
+ Dependencies
61
+ ++++++++++++
62
+
63
+ - Bumped ``django-reversion`` from ``~=6.0.0`` to `~=6.3.0
64
+ <https://github.com/etianen/django-reversion/blob/v6.3.0/CHANGELOG.rst>`_.
65
+ - Bumped ``netjsonconfig`` from ``~=1.2.0`` to `~=1.3.0
66
+ <https://github.com/openwisp/netjsonconfig/blob/master/CHANGES.rst>`__.
67
+ - Bumped ``django-x509`` from ``~=1.3.0`` to `~=1.4.0
68
+ <https://github.com/openwisp/django-x509/blob/master/CHANGES.rst>`_.
69
+ - Bumped ``django-loci`` from ``~=1.2.0`` to `~=1.3.0
70
+ <https://github.com/openwisp/django-loci/blob/master/CHANGES.rst>`__.
71
+ - Bumped ``django-flat-json-widget`` from ``~=0.4.0`` to `~=0.5.0
72
+ <https://github.com/openwisp/django-flat-json-widget/blob/master/CHANGES.rst>`_.
73
+ - Bumped ``openwisp-users`` from ``~=1.2.0`` to `~=1.3.0
74
+ <https://github.com/openwisp/openwisp-users/blob/master/CHANGES.rst>`__.
75
+ - Bumped ``openwisp-utils[celery,channels]`` from ``~=1.2.0`` to `~=1.3.0
76
+ <https://github.com/openwisp/openwisp-utils/blob/master/CHANGES.rst>`__.
77
+ - Bumped ``openwisp-notifications`` from ``~=1.2.0`` to `~=1.3.0
78
+ <https://github.com/openwisp/openwisp-notifications/blob/master/CHANGES.rst>`__.
79
+ - Bumped ``openwisp-ipam`` from ``~=1.2.0`` to `~=1.3.1
80
+ <https://github.com/openwisp/openwisp-ipam/blob/master/CHANGES.rst>`_.
81
+ - Bumped ``djangorestframework-gis`` from ``~=1.2.0`` to `~=1.3.0
82
+ <https://github.com/openwisp/django-rest-framework-gis/blob/master/CHANGES.rst>`__.
83
+ - Bumped ``paramiko`` from ``~=4.0.0`` to `~=5.0.0
84
+ <https://www.paramiko.org/changelog.html>`_.
85
+ - Bumped ``scp`` from ``~=0.15.0`` to `~=0.16.1
86
+ <https://github.com/jbardin/scp.py/releases/tag/v0.16.1>`_.
87
+ - Bumped ``django-import-export`` from ``~=4.3.10`` to `~=4.4.1
88
+ <https://django-import-export.readthedocs.io/en/latest/changelog.html>`_.
89
+ - Added `geoip2>=5.3.0,<6.0.0
90
+ <https://github.com/maxmind/GeoIP2-python/releases/tag/v5.3.0>`_.
91
+ - Dropped support for Django ``4.2``.
92
+ - Dropped support for Python ``3.9``.
93
+
94
+ Bugfixes
95
+ ~~~~~~~~
96
+
97
+ - Fixed `subnet-division rules allocating addresses already in use
98
+ <https://github.com/openwisp/openwisp-controller/issues/1464>`_ by
99
+ respecting existing child prefixes and pre-allocated IP addresses.
100
+ - Fixed background command and connection operations potentially restoring
101
+ objects that had been deleted concurrently or causing foreign key
102
+ errors.
103
+ - Fixed VPN client synchronization when switching VPN templates,
104
+ preventing inconsistent client state during repeated template updates.
105
+ - Fixed SSH connections remaining open when ``update_config()`` failed.
106
+ - Fixed `malformed UUID URLs returning HTTP 500 instead of HTTP 404
107
+ <https://github.com/openwisp/openwisp-controller/issues/682>`_.
108
+ - Fixed the Device admin delete confirmation page when OpenWISP Controller
109
+ is extended by downstream applications.
110
+ - Fixed a typo in the ``update_strategy`` validation error message.
111
+
112
+ Version 1.2.3 [2026-04-09]
113
+ --------------------------
114
+
115
+ Bugfixes
116
+ ~~~~~~~~
117
+
118
+ - Corrected initial field value assignment in ``AbstractDevice`` class
119
+ - Fixed duplicate template entries in Device admin
120
+ - Invalidate Config checksum after subnet provisioning
121
+ - Fixed concurrent ``update_config`` detection `#1204
122
+ <https://github.com/openwisp/openwisp-controller/issues/1204>`_
123
+
4
124
  Version 1.2.2 [2026-03-06]
5
125
  --------------------------
6
126
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openwisp-controller
3
- Version: 1.2.2
3
+ Version: 1.3
4
4
  Summary: OpenWISP Controller
5
5
  Home-page: http://openwisp.org
6
6
  Download-URL: https://github.com/openwisp/openwisp-controller/releases
@@ -20,23 +20,24 @@ Classifier: Framework :: Django
20
20
  Classifier: Programming Language :: Python :: 3
21
21
  License-File: LICENSE
22
22
  Requires-Dist: django-sortedm2m~=4.0.0
23
- Requires-Dist: django-reversion~=6.0.0
23
+ Requires-Dist: django-reversion~=6.3.0
24
24
  Requires-Dist: django-taggit~=6.1.0
25
- Requires-Dist: netjsonconfig~=1.2.0
26
- Requires-Dist: django-x509~=1.3.0
27
- Requires-Dist: django-loci~=1.2.0
28
- Requires-Dist: django-flat-json-widget~=0.4.0
29
- Requires-Dist: openwisp-users~=1.2.0
30
- Requires-Dist: openwisp-utils[celery,channels]~=1.2.0
31
- Requires-Dist: openwisp-notifications~=1.2.0
32
- Requires-Dist: openwisp-ipam~=1.2.0
33
- Requires-Dist: djangorestframework-gis~=1.2.0
34
- Requires-Dist: paramiko~=4.0.0
35
- Requires-Dist: scp~=0.15.0
25
+ Requires-Dist: netjsonconfig~=1.3.0
26
+ Requires-Dist: django-x509~=1.4.0
27
+ Requires-Dist: django-loci~=1.3.0
28
+ Requires-Dist: django-flat-json-widget~=0.5.0
29
+ Requires-Dist: openwisp-users~=1.3.0
30
+ Requires-Dist: openwisp-utils[celery,channels]~=1.3.0
31
+ Requires-Dist: openwisp-notifications~=1.3.0
32
+ Requires-Dist: openwisp-ipam~=1.3.1
33
+ Requires-Dist: djangorestframework-gis~=1.3.0
34
+ Requires-Dist: paramiko~=5.0.0
35
+ Requires-Dist: scp~=0.16.1
36
36
  Requires-Dist: django-cache-memoize~=0.2.1
37
37
  Requires-Dist: shortuuid~=1.0.13
38
38
  Requires-Dist: netaddr~=1.3.0
39
- Requires-Dist: django-import-export~=4.3.10
39
+ Requires-Dist: django-import-export~=4.4.1
40
+ Requires-Dist: geoip2<6.0.0,>=5.3.0
40
41
  Dynamic: author
41
42
  Dynamic: author-email
42
43
  Dynamic: classifier
@@ -1,4 +1,4 @@
1
- VERSION = (1, 2, 2, "final")
1
+ VERSION = (1, 3, 0, "final")
2
2
  __version__ = VERSION # alias
3
3
 
4
4
 
@@ -15,12 +15,14 @@ from django.core.exceptions import (
15
15
  ObjectDoesNotExist,
16
16
  ValidationError,
17
17
  )
18
+ from django.db import models
19
+ from django.db.models.functions import Cast
18
20
  from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
19
21
  from django.http.response import HttpResponseForbidden
20
22
  from django.shortcuts import get_object_or_404
21
23
  from django.template.loader import get_template
22
24
  from django.template.response import TemplateResponse
23
- from django.urls import path, re_path, reverse
25
+ from django.urls import NoReverseMatch, path, reverse
24
26
  from django.utils.html import format_html, mark_safe
25
27
  from django.utils.translation import gettext_lazy as _
26
28
  from django.utils.translation import ngettext_lazy
@@ -31,12 +33,14 @@ from openwisp_ipam.filters import SubnetFilter
31
33
  from swapper import load_model
32
34
 
33
35
  from openwisp_controller.config.views import get_default_values, get_relevant_templates
36
+ from openwisp_controller.config.whois.utils import get_whois_info
34
37
  from openwisp_users.admin import OrganizationAdmin
35
38
  from openwisp_users.multitenancy import MultitenantOrgFilter
36
39
  from openwisp_utils.admin import (
37
40
  AlwaysHasChangedMixin,
41
+ BlockDeleteAllowCascadeMixin,
42
+ CopyableFieldsAdmin,
38
43
  TimeReadonlyAdminMixin,
39
- UUIDAdmin,
40
44
  )
41
45
 
42
46
  from ..admin import MultitenantAdminMixin
@@ -49,6 +53,7 @@ from .widgets import DeviceGroupJsonSchemaWidget, JsonSchemaWidget
49
53
 
50
54
  logger = logging.getLogger(__name__)
51
55
  prefix = "config/"
56
+ whois_prefix = "whois/"
52
57
  Config = load_model("config", "Config")
53
58
  Device = load_model("config", "Device")
54
59
  DeviceGroup = load_model("config", "DeviceGroup")
@@ -111,7 +116,7 @@ class BaseConfigAdmin(BaseAdmin):
111
116
 
112
117
  class Media:
113
118
  css = {"all": (f"{prefix}css/admin.css",)}
114
- js = list(UUIDAdmin.Media.js) + [
119
+ js = list(CopyableFieldsAdmin.Media.js) + [
115
120
  f"{prefix}js/{file_}"
116
121
  for file_ in ("preview.js", "unsaved_changes.js", "switcher.js")
117
122
  ]
@@ -137,16 +142,15 @@ class BaseConfigAdmin(BaseAdmin):
137
142
  if not issubclass(self.model, AbstractVpn):
138
143
  ctx["CONFIG_BACKEND_FIELD_SHOWN"] = app_settings.CONFIG_BACKEND_FIELD_SHOWN
139
144
  if pk:
140
- ctx["download_url"] = reverse("{0}_download".format(prefix), args=[pk])
141
145
  try:
146
+ download_url = reverse("{0}_download".format(prefix), args=[pk])
142
147
  has_config = True
143
148
  if self.model.__name__ == "Device":
144
149
  has_config = self.model.objects.get(pk=pk)._has_config()
145
- except (ObjectDoesNotExist, ValidationError):
150
+ except (ObjectDoesNotExist, ValidationError, NoReverseMatch):
146
151
  raise Http404()
147
152
  else:
148
- if not has_config:
149
- ctx["download_url"] = None
153
+ ctx["download_url"] = download_url if has_config else None
150
154
  return ctx
151
155
 
152
156
  def add_view(self, request, form_url="", extra_context=None):
@@ -167,8 +171,8 @@ class BaseConfigAdmin(BaseAdmin):
167
171
  options = getattr(self.model, "_meta")
168
172
  url_prefix = "{0}_{1}".format(options.app_label, options.model_name)
169
173
  return [
170
- re_path(
171
- r"^download/(?P<pk>[^/]+)/$",
174
+ path(
175
+ "download/<uuid_any:pk>/",
172
176
  self.admin_site.admin_view(self.download_view),
173
177
  name="{0}_download".format(url_prefix),
174
178
  ),
@@ -177,8 +181,8 @@ class BaseConfigAdmin(BaseAdmin):
177
181
  self.admin_site.admin_view(self.preview_view),
178
182
  name="{0}_preview".format(url_prefix),
179
183
  ),
180
- re_path(
181
- r"^(?P<pk>[^/]+)/context\.json$",
184
+ path(
185
+ "<uuid_any:pk>/context.json",
182
186
  self.admin_site.admin_view(self.context_view),
183
187
  name="{0}_context".format(url_prefix),
184
188
  ),
@@ -215,6 +219,15 @@ class BaseConfigAdmin(BaseAdmin):
215
219
  key = "{relation}_id".format(relation=key)
216
220
  # pass non-empty string or None
217
221
  kwargs[key] = value or None
222
+ # parse JSON strings for JSONField fields
223
+ elif isinstance(field, models.JSONField) and isinstance(value, str):
224
+ if not value:
225
+ kwargs[key] = None
226
+ else:
227
+ try:
228
+ kwargs[key] = json.loads(value)
229
+ except json.JSONDecodeError:
230
+ kwargs[key] = value
218
231
  # put regular field values in kwargs dict
219
232
  else:
220
233
  kwargs[key] = value
@@ -498,11 +511,12 @@ class ChangeDeviceGroupForm(forms.Form):
498
511
  )
499
512
 
500
513
 
501
- class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
514
+ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, CopyableFieldsAdmin):
502
515
  change_form_template = "admin/config/device/change_form.html"
503
516
  delete_selected_confirmation_template = (
504
517
  "admin/config/device/delete_selected_confirmation.html"
505
518
  )
519
+ delete_confirmation_template = "admin/config/device/delete_confirmation.html"
506
520
  list_display = [
507
521
  "name",
508
522
  "backend",
@@ -548,6 +562,7 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
548
562
  "created",
549
563
  "modified",
550
564
  ]
565
+ copyable_fields = ["uuid"]
551
566
  inlines = [ConfigInline]
552
567
  conditional_inlines = []
553
568
  actions = [
@@ -569,12 +584,21 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
569
584
  fields.insert(0, "hardware_id")
570
585
  list_select_related = ("config", "organization")
571
586
 
572
- class Media(BaseConfigAdmin.Media):
587
+ # overriding media property to allow testing in isolation
588
+ # as class Media is evaluated at import time making the
589
+ # settings not overridable in tests
590
+ @property
591
+ def media(self):
573
592
  js = BaseConfigAdmin.Media.js + [
574
593
  f"{prefix}js/tabs.js",
575
594
  f"{prefix}js/management_ip.js",
576
595
  f"{prefix}js/relevant_templates.js",
577
596
  ]
597
+ css = BaseConfigAdmin.Media.css["all"]
598
+ if app_settings.WHOIS_CONFIGURED:
599
+ js.append(f"{whois_prefix}js/whois.js")
600
+ css += (f"{whois_prefix}css/whois.css",)
601
+ return super().media + forms.Media(js=js, css={"all": css})
578
602
 
579
603
  def has_change_permission(self, request, obj=None):
580
604
  perm = super().has_change_permission(request)
@@ -586,8 +610,70 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
586
610
  perm = super().has_delete_permission(request)
587
611
  if not obj:
588
612
  return perm
613
+ if BlockDeleteAllowCascadeMixin.is_admin_cascade_delete_request(self, request):
614
+ self._add_active_device_delete_warning(request, obj)
615
+ return perm
589
616
  return perm and obj.is_deactivated()
590
617
 
618
+ def _add_active_device_delete_warning(self, request, obj):
619
+ if getattr(request, "_active_device_delete_warning_checked", False):
620
+ return
621
+ resolver_match = getattr(request, "resolver_match", None)
622
+ url_name = getattr(resolver_match, "url_name", None)
623
+ opts = Organization._meta
624
+ organization_delete_url_name = f"{opts.app_label}_{opts.model_name}_delete"
625
+ organization_changelist_url_name = (
626
+ f"{opts.app_label}_{opts.model_name}_changelist"
627
+ )
628
+ active_organizations = 0
629
+ if request.method == "GET" and url_name == organization_delete_url_name:
630
+ request._active_device_delete_warning_checked = True
631
+ active_organizations = (
632
+ Device.objects.filter(
633
+ organization_id=obj.organization_id,
634
+ _is_deactivated=False,
635
+ )
636
+ .values("organization_id")
637
+ .distinct()
638
+ .count()
639
+ )
640
+ elif (
641
+ url_name == organization_changelist_url_name
642
+ and request.POST.get("action") == "delete_selected"
643
+ and not request.POST.get("post")
644
+ ):
645
+ request._active_device_delete_warning_checked = True
646
+ organizations = self.admin_site.get_model_admin(Organization).get_queryset(
647
+ request
648
+ )
649
+ active_organizations = (
650
+ Device.objects.filter(
651
+ organization_id__in=organizations.filter(
652
+ pk__in=request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
653
+ ),
654
+ _is_deactivated=False,
655
+ )
656
+ .values("organization_id")
657
+ .distinct()
658
+ .count()
659
+ )
660
+ if not active_organizations:
661
+ return
662
+ self.message_user(
663
+ request,
664
+ ngettext_lazy(
665
+ "This organization contains active devices. It is highly "
666
+ "recommended to deactivate them before deleting the organization "
667
+ "to ensure sensitive configuration data is disposed of safely.",
668
+ "%(count)d organizations contain active devices. It is highly "
669
+ "recommended to deactivate them before deleting the organizations "
670
+ "to ensure sensitive configuration data is disposed of safely.",
671
+ active_organizations,
672
+ )
673
+ % {"count": active_organizations},
674
+ messages.WARNING,
675
+ )
676
+
591
677
  def save_form(self, request, form, change):
592
678
  self._state_adding = form.instance._state.adding
593
679
  return super().save_form(request, form, change)
@@ -656,6 +742,7 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
656
742
  form = ChangeDeviceGroupForm(data=request.POST, org_id=org_id)
657
743
  if form.is_valid():
658
744
  group = form.cleaned_data["device_group"]
745
+ queryset = queryset.filter(_is_deactivated=False)
659
746
  # Evaluate queryset to store old group id
660
747
  old_group_qs = list(queryset)
661
748
  queryset.update(group=group or None)
@@ -683,8 +770,7 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
683
770
  "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
684
771
  "opts": self.model._meta,
685
772
  "changelist_url": (
686
- f"{request.resolver_match.app_name}:"
687
- f"{request.resolver_match.url_name}"
773
+ f"{request.resolver_match.app_name}:{request.resolver_match.url_name}"
688
774
  ),
689
775
  }
690
776
 
@@ -928,6 +1014,10 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
928
1014
  ),
929
1015
  }
930
1016
  )
1017
+ # passing the whois details to the context to avoid
1018
+ # the need to make an additional request in the js
1019
+ if data := get_whois_info(pk):
1020
+ ctx["device_whois_details"] = data
931
1021
  return ctx
932
1022
 
933
1023
  def add_view(self, request, form_url="", extra_context=None):
@@ -1046,7 +1136,7 @@ class TemplateAdmin(MultitenantAdminMixin, BaseConfigAdmin, SystemDefinedVariabl
1046
1136
  "required",
1047
1137
  "created",
1048
1138
  ]
1049
- search_fields = ["name"]
1139
+ search_fields = ["name", "notes"]
1050
1140
  multitenant_shared_relations = ("vpn",)
1051
1141
  fields = [
1052
1142
  "name",
@@ -1058,6 +1148,7 @@ class TemplateAdmin(MultitenantAdminMixin, BaseConfigAdmin, SystemDefinedVariabl
1058
1148
  "tags",
1059
1149
  "default",
1060
1150
  "required",
1151
+ "notes",
1061
1152
  "system_context",
1062
1153
  "default_values",
1063
1154
  "config",
@@ -1089,8 +1180,7 @@ class TemplateAdmin(MultitenantAdminMixin, BaseConfigAdmin, SystemDefinedVariabl
1089
1180
  validated_org = Organization.objects.get(pk=organization)
1090
1181
  except (ValidationError, Organization.DoesNotExist) as e:
1091
1182
  logger.warning(
1092
- "Detected tampering in clone template "
1093
- f"form by user {user}: {e}"
1183
+ f"Detected tampering in clone template form by user {user}: {e}"
1094
1184
  )
1095
1185
  return
1096
1186
  if not user.is_superuser and not user.is_manager(organization):
@@ -1202,7 +1292,10 @@ class VpnForm(forms.ModelForm):
1202
1292
 
1203
1293
 
1204
1294
  class VpnAdmin(
1205
- MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin, SystemDefinedVariableMixin
1295
+ MultitenantAdminMixin,
1296
+ BaseConfigAdmin,
1297
+ CopyableFieldsAdmin,
1298
+ SystemDefinedVariableMixin,
1206
1299
  ):
1207
1300
  form = VpnForm
1208
1301
  list_display = [
@@ -1223,6 +1316,7 @@ class VpnAdmin(
1223
1316
  ]
1224
1317
  search_fields = ["id", "name", "host", "key"]
1225
1318
  readonly_fields = ["id", "uuid", "system_context"]
1319
+ copyable_fields = ["uuid"]
1226
1320
  multitenant_shared_relations = ("ca", "cert", "subnet")
1227
1321
  autocomplete_fields = ["ip", "subnet"]
1228
1322
  fields = [
@@ -1293,12 +1387,19 @@ class DeviceGroupAdmin(MultitenantAdminMixin, BaseAdmin):
1293
1387
  "created",
1294
1388
  "modified",
1295
1389
  ]
1296
- search_fields = ["name", "description", "meta_data"]
1390
+ search_fields = ["name", "description", "_meta_data_text"]
1297
1391
  list_filter = [MultitenantOrgFilter, DeviceGroupFilter]
1298
1392
  multitenant_shared_relations = ("templates",)
1299
1393
 
1394
+ def get_search_results(self, request, queryset, search_term):
1395
+ if search_term:
1396
+ queryset = queryset.annotate(
1397
+ _meta_data_text=Cast("meta_data", output_field=models.TextField()),
1398
+ )
1399
+ return super().get_search_results(request, queryset, search_term)
1400
+
1300
1401
  class Media:
1301
- js = list(UUIDAdmin.Media.js) + [
1402
+ js = list(CopyableFieldsAdmin.Media.js) + [
1302
1403
  f"{prefix}js/relevant_templates.js",
1303
1404
  ]
1304
1405
  css = {"all": (f"{prefix}css/admin.css",)}
@@ -1362,18 +1463,29 @@ class OrganizationLimitsInline(admin.StackedInline):
1362
1463
 
1363
1464
 
1364
1465
  limits_inline_position = 0
1365
- if getattr(app_settings, "REGISTRATION_ENABLED", True):
1366
1466
 
1367
- class ConfigSettingsForm(AlwaysHasChangedMixin, forms.ModelForm):
1368
- class Meta:
1369
- widgets = {"context": FlatJsonWidget}
1370
1467
 
1371
- class ConfigSettingsInline(admin.StackedInline):
1372
- model = OrganizationConfigSettings
1373
- form = ConfigSettingsForm
1468
+ class ConfigSettingsForm(AlwaysHasChangedMixin, forms.ModelForm):
1469
+ class Meta:
1470
+ widgets = {"context": FlatJsonWidget}
1471
+
1472
+
1473
+ class ConfigSettingsInline(admin.StackedInline):
1474
+ model = OrganizationConfigSettings
1475
+ form = ConfigSettingsForm
1476
+
1477
+ def get_fields(self, request, obj=None):
1478
+ fields = []
1479
+ if app_settings.REGISTRATION_ENABLED:
1480
+ fields += ["registration_enabled", "shared_secret"]
1481
+ if app_settings.WHOIS_CONFIGURED:
1482
+ fields += ["whois_enabled"]
1483
+ fields += ["context"]
1484
+ return fields
1485
+
1374
1486
 
1375
- OrganizationAdmin.save_on_top = True
1376
- OrganizationAdmin.inlines.insert(0, ConfigSettingsInline)
1377
- limits_inline_position = 1
1487
+ OrganizationAdmin.save_on_top = True
1488
+ OrganizationAdmin.inlines.insert(0, ConfigSettingsInline)
1489
+ limits_inline_position = 1
1378
1490
 
1379
1491
  OrganizationAdmin.inlines.insert(limits_inline_position, OrganizationLimitsInline)
@@ -9,6 +9,7 @@ from openwisp_utils.api.serializers import ValidatedModelSerializer
9
9
 
10
10
  from ...serializers import BaseSerializer
11
11
  from .. import settings as app_settings
12
+ from ..whois.mixins import WHOISMixin
12
13
 
13
14
  Template = load_model("config", "Template")
14
15
  Vpn = load_model("config", "Vpn")
@@ -40,6 +41,7 @@ class TemplateSerializer(BaseSerializer):
40
41
  "tags",
41
42
  "default",
42
43
  "required",
44
+ "notes",
43
45
  "default_values",
44
46
  "config",
45
47
  "created",
@@ -70,6 +72,7 @@ class TemplateSerializer(BaseSerializer):
70
72
 
71
73
  class VpnSerializer(BaseSerializer):
72
74
  config = serializers.JSONField(initial={})
75
+ ip = serializers.PrimaryKeyRelatedField(read_only=True)
73
76
  include_shared = True
74
77
 
75
78
  class Meta(BaseMeta):
@@ -83,6 +86,10 @@ class VpnSerializer(BaseSerializer):
83
86
  "ca",
84
87
  "cert",
85
88
  "backend",
89
+ "subnet",
90
+ "ip",
91
+ "webhook_endpoint",
92
+ "auth_token",
86
93
  "notes",
87
94
  "dh",
88
95
  "config",
@@ -90,6 +97,28 @@ class VpnSerializer(BaseSerializer):
90
97
  "modified",
91
98
  ]
92
99
 
100
+ def validate(self, data):
101
+ """Validates the VPN and clears the certificate if CA is changed.
102
+
103
+ When the CA is updated on an existing VPN, the current certificate
104
+ (which was issued by the old CA) becomes invalid. This method clears
105
+ the certificate so the model's ``save()`` method can auto-create a
106
+ new one for the new CA.
107
+ """
108
+ if self.instance and "ca" in data and self.instance.cert:
109
+ # if the CA is changed and new cert not supplied,
110
+ # allow creating a new certificate automatically
111
+ if data["ca"] and self.instance.cert.ca != data["ca"]:
112
+ data["cert"] = None
113
+ return super().validate(data)
114
+
115
+
116
+ class VpnListSerializer(VpnSerializer):
117
+ class Meta(VpnSerializer.Meta):
118
+ fields = VpnSerializer.Meta.fields[:]
119
+ fields.remove("webhook_endpoint")
120
+ fields.remove("auth_token")
121
+
93
122
 
94
123
  class FilterTemplatesByOrganization(serializers.PrimaryKeyRelatedField):
95
124
  def get_queryset(self):
@@ -274,7 +303,7 @@ class DeviceDetailConfigSerializer(BaseConfigSerializer):
274
303
  templates = FilterTemplatesByOrganization(many=True)
275
304
 
276
305
 
277
- class DeviceDetailSerializer(DeviceConfigSerializer):
306
+ class DeviceDetailSerializer(WHOISMixin, DeviceConfigSerializer):
278
307
  config = DeviceDetailConfigSerializer(allow_null=True)
279
308
  is_deactivated = serializers.BooleanField(read_only=True)
280
309