nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__py3-none-any.whl

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.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (420) hide show
  1. nautobot/apps/choices.py +0 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +9 -1
  5. nautobot/circuits/filters.py +3 -2
  6. nautobot/circuits/navigation.py +3 -2
  7. nautobot/circuits/templates/circuits/circuit.html +1 -1
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination.html +1 -1
  10. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  11. nautobot/circuits/templates/circuits/circuittype.html +1 -1
  12. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  13. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  14. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  15. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  16. nautobot/cloud/filters.py +1 -1
  17. nautobot/cloud/navigation.py +3 -2
  18. nautobot/core/api/schema.py +1 -1
  19. nautobot/core/api/serializers.py +6 -1
  20. nautobot/core/api/urls.py +1 -0
  21. nautobot/core/api/views.py +8 -0
  22. nautobot/core/apps/__init__.py +11 -10
  23. nautobot/core/celery/__init__.py +3 -5
  24. nautobot/core/checks.py +46 -0
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
  26. nautobot/core/cli/migrate_deprecated_templates.py +200 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/forms/forms.py +1 -1
  30. nautobot/core/jobs/__init__.py +6 -3
  31. nautobot/core/jobs/groups.py +31 -1
  32. nautobot/core/management/commands/generate_test_data.py +28 -9
  33. nautobot/core/models/generics.py +9 -1
  34. nautobot/core/models/tree_queries.py +10 -5
  35. nautobot/core/settings.py +18 -12
  36. nautobot/core/settings.yaml +13 -7
  37. nautobot/core/signals.py +12 -1
  38. nautobot/core/tables.py +13 -6
  39. nautobot/core/templates/40x.html +1 -1
  40. nautobot/core/templates/500.html +2 -2
  41. nautobot/core/templates/admin/config/config.html +12 -12
  42. nautobot/core/templates/admin/index.html +3 -3
  43. nautobot/core/templates/buttons/export.html +1 -1
  44. nautobot/core/templates/components/button/dropdown.html +5 -3
  45. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  46. nautobot/core/templates/components/panel/panel.html +3 -3
  47. nautobot/core/templates/components/tab/content_wrapper.html +2 -3
  48. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  49. nautobot/core/templates/echarts/echarts.html +1 -1
  50. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  51. nautobot/core/templates/generic/object_bulk_create.html +4 -3
  52. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  53. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  54. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  55. nautobot/core/templates/generic/object_create.html +5 -4
  56. nautobot/core/templates/generic/object_import.html +2 -1
  57. nautobot/core/templates/generic/object_list.html +12 -4
  58. nautobot/core/templates/generic/object_notes.html +5 -3
  59. nautobot/core/templates/generic/object_retrieve.html +2 -3
  60. nautobot/core/templates/graphene/graphiql.html +7 -7
  61. nautobot/core/templates/home.html +1 -1
  62. nautobot/core/templates/import_success.html +2 -1
  63. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  64. nautobot/core/templates/inc/created_updated.html +7 -3
  65. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  66. nautobot/core/templates/inc/form_static_field.html +6 -0
  67. nautobot/core/templates/inc/header.html +1 -1
  68. nautobot/core/templates/inc/image_attachments.html +2 -1
  69. nautobot/core/templates/inc/nav_menu.html +2 -1
  70. nautobot/core/templates/inc/search_panel.html +4 -4
  71. nautobot/core/templates/login.html +4 -2
  72. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  73. nautobot/core/templates/redoc_ui.html +7 -0
  74. nautobot/core/templates/search.html +1 -1
  75. nautobot/core/templates/swagger_ui.html +17 -3
  76. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  77. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  78. nautobot/core/templates/utilities/obj_table.html +10 -2
  79. nautobot/core/templates/utilities/render_field.html +7 -7
  80. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  81. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +4 -4
  82. nautobot/core/templates/utilities/theme_preview.html +16 -3
  83. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  84. nautobot/core/templatetags/helpers.py +52 -6
  85. nautobot/core/testing/api.py +68 -9
  86. nautobot/core/testing/filters.py +0 -23
  87. nautobot/core/testing/integration.py +23 -10
  88. nautobot/core/testing/mixins.py +2 -0
  89. nautobot/core/testing/views.py +4 -0
  90. nautobot/core/tests/integration/test_app_home.py +34 -30
  91. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  92. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  93. nautobot/core/tests/runner.py +9 -1
  94. nautobot/core/tests/test_api.py +5 -3
  95. nautobot/core/tests/test_breadcrumbs.py +6 -7
  96. nautobot/core/tests/test_checks.py +28 -0
  97. nautobot/core/tests/test_cli.py +40 -0
  98. nautobot/core/tests/test_config.py +2 -1
  99. nautobot/core/tests/test_forms.py +55 -13
  100. nautobot/core/tests/test_jobs.py +75 -1
  101. nautobot/core/tests/test_nautobot_server.py +2 -0
  102. nautobot/core/tests/test_navigations.py +76 -1
  103. nautobot/core/tests/test_patch_social_django.py +42 -0
  104. nautobot/core/tests/test_tables.py +3 -1
  105. nautobot/core/tests/test_templatetags_helpers.py +53 -13
  106. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  107. nautobot/core/tests/test_tree_queries.py +14 -1
  108. nautobot/core/tests/test_ui.py +1 -1
  109. nautobot/core/tests/test_utils.py +31 -4
  110. nautobot/core/tests/test_views.py +159 -31
  111. nautobot/core/ui/breadcrumbs.py +2 -12
  112. nautobot/core/ui/choices.py +142 -10
  113. nautobot/core/ui/constants.py +76 -12
  114. nautobot/core/ui/object_detail.py +92 -12
  115. nautobot/core/urls.py +12 -1
  116. nautobot/core/utils/cache.py +2 -1
  117. nautobot/core/utils/filtering.py +17 -17
  118. nautobot/core/utils/lookup.py +3 -8
  119. nautobot/core/utils/module_loading.py +21 -0
  120. nautobot/core/utils/patch_social_django.py +128 -0
  121. nautobot/core/views/__init__.py +38 -1
  122. nautobot/core/views/generic.py +3 -3
  123. nautobot/core/views/mixins.py +15 -3
  124. nautobot/core/views/renderers.py +2 -0
  125. nautobot/core/views/viewsets.py +2 -1
  126. nautobot/data_validation/apps.py +1 -5
  127. nautobot/data_validation/custom_validators.py +4 -4
  128. nautobot/data_validation/filters.py +1 -1
  129. nautobot/data_validation/forms.py +40 -0
  130. nautobot/data_validation/migrations/0001_initial.py +0 -7
  131. nautobot/data_validation/migrations/0002_data_migration_from_app.py +0 -12
  132. nautobot/data_validation/models.py +16 -7
  133. nautobot/data_validation/navigation.py +8 -1
  134. nautobot/data_validation/tables.py +12 -5
  135. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  136. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  137. nautobot/data_validation/tests/__init__.py +2 -2
  138. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  139. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  140. nautobot/data_validation/tests/test_filters.py +8 -6
  141. nautobot/data_validation/tests/test_models.py +15 -0
  142. nautobot/data_validation/tests/test_views.py +190 -32
  143. nautobot/data_validation/urls.py +2 -5
  144. nautobot/data_validation/views.py +73 -40
  145. nautobot/dcim/api/serializers.py +0 -13
  146. nautobot/dcim/apps.py +4 -0
  147. nautobot/dcim/choices.py +16 -0
  148. nautobot/dcim/custom_validators.py +84 -0
  149. nautobot/dcim/filter_mixins.py +353 -4
  150. nautobot/dcim/{filters/__init__.py → filters.py} +2 -35
  151. nautobot/dcim/forms.py +1 -1
  152. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  153. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  154. nautobot/dcim/models/device_components.py +81 -68
  155. nautobot/dcim/models/devices.py +13 -16
  156. nautobot/dcim/navigation.py +7 -6
  157. nautobot/dcim/tables/devices.py +3 -0
  158. nautobot/dcim/tables/template_code.py +14 -14
  159. nautobot/dcim/templates/dcim/cable.html +2 -61
  160. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  161. nautobot/dcim/templates/dcim/cable_edit.html +2 -5
  162. nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
  163. nautobot/dcim/templates/dcim/cable_trace.html +1 -3
  164. nautobot/dcim/templates/dcim/cable_update.html +5 -0
  165. nautobot/dcim/templates/dcim/consoleport.html +6 -5
  166. nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
  167. nautobot/dcim/templates/dcim/device/config.html +2 -2
  168. nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
  169. nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
  170. nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
  171. nautobot/dcim/templates/dcim/device/frontports.html +1 -1
  172. nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
  173. nautobot/dcim/templates/dcim/device/inventory.html +1 -1
  174. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  175. nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
  176. nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
  177. nautobot/dcim/templates/dcim/device/powerports.html +1 -1
  178. nautobot/dcim/templates/dcim/device/rearports.html +1 -1
  179. nautobot/dcim/templates/dcim/device/status.html +8 -8
  180. nautobot/dcim/templates/dcim/device/wireless.html +1 -1
  181. nautobot/dcim/templates/dcim/device.html +1 -1
  182. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  183. nautobot/dcim/templates/dcim/device_create.html +5 -3
  184. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  185. nautobot/dcim/templates/dcim/device_list.html +73 -10
  186. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  187. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  188. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  189. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  190. nautobot/dcim/templates/dcim/frontport.html +9 -8
  191. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  192. nautobot/dcim/templates/dcim/interface.html +26 -6
  193. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  194. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  195. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  196. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  197. nautobot/dcim/templates/dcim/location_retrieve.html +1 -242
  198. nautobot/dcim/templates/dcim/module/base.html +49 -9
  199. nautobot/dcim/templates/dcim/module_list.html +57 -8
  200. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  201. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  202. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  203. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  204. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  205. nautobot/dcim/templates/dcim/powerport.html +5 -4
  206. nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
  207. nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
  208. nautobot/dcim/templates/dcim/rearport.html +7 -6
  209. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  210. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  211. nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
  212. nautobot/dcim/tests/integration/test_controller.py +1 -0
  213. nautobot/dcim/tests/test_api.py +8 -0
  214. nautobot/dcim/tests/test_custom_validators.py +229 -0
  215. nautobot/dcim/tests/test_filters.py +12 -6
  216. nautobot/dcim/tests/test_models.py +63 -4
  217. nautobot/dcim/tests/test_views.py +63 -22
  218. nautobot/dcim/urls.py +64 -21
  219. nautobot/dcim/utils.py +3 -3
  220. nautobot/dcim/views.py +547 -273
  221. nautobot/extras/api/views.py +9 -1
  222. nautobot/extras/choices.py +2 -13
  223. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  224. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  225. nautobot/extras/{filters/__init__.py → filters.py} +14 -46
  226. nautobot/extras/forms/forms.py +5 -13
  227. nautobot/extras/forms/mixins.py +0 -41
  228. nautobot/extras/management/__init__.py +9 -0
  229. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  230. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  231. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  232. nautobot/extras/models/__init__.py +1 -2
  233. nautobot/extras/models/approvals.py +22 -13
  234. nautobot/extras/models/contacts.py +2 -0
  235. nautobot/extras/models/groups.py +44 -5
  236. nautobot/extras/models/jobs.py +59 -1
  237. nautobot/extras/models/mixins.py +28 -0
  238. nautobot/extras/models/models.py +13 -0
  239. nautobot/extras/models/secrets.py +1 -0
  240. nautobot/extras/models/statuses.py +0 -15
  241. nautobot/extras/navigation.py +13 -9
  242. nautobot/extras/plugins/__init__.py +33 -55
  243. nautobot/extras/plugins/tables.py +3 -3
  244. nautobot/extras/plugins/urls.py +2 -21
  245. nautobot/extras/plugins/utils.py +1 -33
  246. nautobot/extras/plugins/views.py +0 -4
  247. nautobot/extras/signals.py +20 -19
  248. nautobot/extras/tables.py +52 -68
  249. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  250. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  251. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  252. nautobot/extras/templates/extras/computedfield.html +1 -1
  253. nautobot/extras/templates/extras/configcontext.html +1 -1
  254. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  255. nautobot/extras/templates/extras/customfield.html +1 -1
  256. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  257. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  258. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  259. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -96
  260. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  261. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  262. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  263. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  264. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  265. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  266. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  267. nautobot/extras/templates/extras/job.html +29 -12
  268. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  269. nautobot/extras/templates/extras/job_edit.html +52 -46
  270. nautobot/extras/templates/extras/job_list.html +29 -25
  271. nautobot/extras/templates/extras/marketplace.html +5 -9
  272. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  273. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  274. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -37
  275. nautobot/extras/templates/extras/plugin_detail.html +26 -21
  276. nautobot/extras/templates/extras/plugins_list.html +16 -26
  277. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  278. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  279. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  280. nautobot/extras/templates/extras/tag.html +1 -1
  281. nautobot/extras/templatetags/custom_links.py +12 -12
  282. nautobot/extras/templatetags/job_buttons.py +14 -12
  283. nautobot/extras/test_jobs/invalid_import.py +9 -0
  284. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  285. nautobot/extras/test_jobs/missing_import.py +11 -0
  286. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  287. nautobot/extras/tests/integration/test_customfields.py +8 -7
  288. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
  289. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  290. nautobot/extras/tests/integration/test_plugins.py +18 -6
  291. nautobot/extras/tests/test_api.py +27 -18
  292. nautobot/extras/tests/test_approvals.py +38 -38
  293. nautobot/extras/tests/test_changelog.py +35 -3
  294. nautobot/extras/tests/test_customfields.py +22 -13
  295. nautobot/extras/tests/test_customfields_filters.py +479 -0
  296. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  297. nautobot/extras/tests/test_filters.py +21 -19
  298. nautobot/extras/tests/test_forms.py +18 -21
  299. nautobot/extras/tests/test_jobs.py +25 -4
  300. nautobot/extras/tests/test_migrations.py +1 -0
  301. nautobot/extras/tests/test_models.py +13 -31
  302. nautobot/extras/tests/test_plugins.py +36 -10
  303. nautobot/extras/tests/test_views.py +31 -30
  304. nautobot/extras/views.py +81 -19
  305. nautobot/ipam/factory.py +7 -0
  306. nautobot/ipam/filter_mixins.py +38 -0
  307. nautobot/ipam/filters.py +27 -38
  308. nautobot/ipam/formfields.py +1 -1
  309. nautobot/ipam/forms.py +6 -3
  310. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  311. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  312. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  313. nautobot/ipam/models.py +29 -2
  314. nautobot/ipam/navigation.py +3 -2
  315. nautobot/ipam/signals.py +71 -0
  316. nautobot/ipam/tables.py +13 -6
  317. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  318. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  319. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  320. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  321. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  322. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  323. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  324. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  325. nautobot/ipam/templates/ipam/service.html +1 -1
  326. nautobot/ipam/templates/ipam/vlan.html +1 -1
  327. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  328. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  329. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  330. nautobot/ipam/tests/test_api.py +13 -6
  331. nautobot/ipam/tests/test_filters.py +10 -0
  332. nautobot/ipam/tests/test_forms.py +1 -1
  333. nautobot/ipam/tests/test_models.py +43 -1
  334. nautobot/ipam/tests/test_tables.py +1 -2
  335. nautobot/ipam/tests/test_utils.py +1 -1
  336. nautobot/ipam/tests/test_views.py +13 -14
  337. nautobot/ipam/ui.py +0 -17
  338. nautobot/ipam/utils/migrations.py +16 -2
  339. nautobot/ipam/utils/testing.py +9 -3
  340. nautobot/ipam/views.py +46 -6
  341. nautobot/project-static/dist/css/nautobot.css +1 -1
  342. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  343. nautobot/project-static/dist/js/nautobot.js +1 -1
  344. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  345. nautobot/project-static/js/cabletrace.js +1 -1
  346. nautobot/project-static/js/interface_filtering.js +20 -16
  347. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  348. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  349. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  350. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  351. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  352. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  353. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  354. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  355. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  356. nautobot/project-static/nautobot-icons/security.svg +3 -0
  357. nautobot/project-static/nautobot-icons/server.svg +1 -1
  358. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  359. nautobot/project-static/nautobot-icons/star.svg +1 -1
  360. nautobot/tenancy/api/serializers.py +1 -0
  361. nautobot/tenancy/api/views.py +2 -1
  362. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  363. nautobot/tenancy/navigation.py +3 -1
  364. nautobot/tenancy/tests/test_filters.py +0 -2
  365. nautobot/tenancy/views.py +2 -1
  366. nautobot/ui/src/js/collapse.js +3 -3
  367. nautobot/ui/src/js/nautobot.js +16 -0
  368. nautobot/ui/src/scss/colors.scss +1 -1
  369. nautobot/ui/src/scss/nautobot.scss +61 -28
  370. nautobot/users/templates/users/profile.html +45 -12
  371. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  372. nautobot/users/tests/test_api.py +4 -0
  373. nautobot/users/views.py +4 -2
  374. nautobot/virtualization/models.py +1 -68
  375. nautobot/virtualization/navigation.py +3 -2
  376. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  377. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  378. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  379. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  380. nautobot/virtualization/tests/test_api.py +3 -0
  381. nautobot/virtualization/tests/test_models.py +44 -4
  382. nautobot/vpn/__init__.py +0 -0
  383. nautobot/vpn/api/serializers.py +113 -0
  384. nautobot/vpn/api/urls.py +19 -0
  385. nautobot/vpn/api/views.py +70 -0
  386. nautobot/vpn/apps.py +8 -0
  387. nautobot/vpn/choices.py +171 -0
  388. nautobot/vpn/factory.py +209 -0
  389. nautobot/vpn/filters.py +233 -0
  390. nautobot/vpn/forms.py +486 -0
  391. nautobot/vpn/homepage.py +19 -0
  392. nautobot/vpn/migrations/0001_initial.py +541 -0
  393. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  394. nautobot/vpn/migrations/__init__.py +0 -0
  395. nautobot/vpn/models.py +527 -0
  396. nautobot/vpn/navigation.py +98 -0
  397. nautobot/vpn/tables.py +380 -0
  398. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  399. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  400. nautobot/vpn/tests/__init__.py +0 -0
  401. nautobot/vpn/tests/test_api.py +341 -0
  402. nautobot/vpn/tests/test_filters.py +139 -0
  403. nautobot/vpn/tests/test_forms.py +294 -0
  404. nautobot/vpn/tests/test_models.py +97 -0
  405. nautobot/vpn/tests/test_views.py +281 -0
  406. nautobot/vpn/urls.py +16 -0
  407. nautobot/vpn/views.py +437 -0
  408. nautobot/wireless/navigation.py +3 -2
  409. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  410. nautobot/wireless/tests/test_api.py +1 -1
  411. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +14 -14
  412. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
  413. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  414. nautobot/data_validation/template_content.py +0 -42
  415. nautobot/dcim/filters/mixins.py +0 -354
  416. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  417. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  418. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  419. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  420. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
nautobot/apps/choices.py CHANGED
@@ -43,7 +43,6 @@ from nautobot.extras.choices import (
43
43
  ButtonClassChoices,
44
44
  CustomFieldFilterLogicChoices,
45
45
  CustomFieldTypeChoices,
46
- CustomLinkButtonClassChoices,
47
46
  DynamicGroupOperatorChoices,
48
47
  JobExecutionType,
49
48
  JobResultStatusChoices,
@@ -79,7 +78,6 @@ __all__ = (
79
78
  "ConsolePortTypeChoices",
80
79
  "CustomFieldFilterLogicChoices",
81
80
  "CustomFieldTypeChoices",
82
- "CustomLinkButtonClassChoices",
83
81
  "DeviceFaceChoices",
84
82
  "DeviceRedundancyGroupFailoverStrategyChoices",
85
83
  "DynamicGroupOperatorChoices",
nautobot/apps/filters.py CHANGED
@@ -28,24 +28,22 @@ from nautobot.core.filters import (
28
28
  TagFilter,
29
29
  TreeNodeMultipleChoiceFilter,
30
30
  )
31
- from nautobot.extras.filters import (
31
+ from nautobot.extras.filter_mixins import (
32
+ ConfigContextRoleFilter,
32
33
  CreatedUpdatedModelFilterSetMixin,
33
34
  CustomFieldModelFilterSetMixin,
34
- NautobotFilterSet,
35
- RelationshipModelFilterSetMixin,
36
- StatusModelFilterSetMixin,
37
- )
38
- from nautobot.extras.filters.mixins import (
39
- ConfigContextRoleFilter,
40
35
  LocalContextModelFilterSetMixin,
41
36
  RelationshipFilter,
37
+ RelationshipModelFilterSetMixin,
42
38
  RoleFilter,
43
39
  RoleModelFilterSetMixin,
44
40
  StatusFilter,
41
+ StatusModelFilterSetMixin,
45
42
  )
43
+ from nautobot.extras.filters import NautobotFilterSet
46
44
  from nautobot.extras.plugins import FilterExtension
47
- from nautobot.ipam.filters import PrefixFilter
48
- from nautobot.tenancy.filters import TenancyModelFilterSetMixin
45
+ from nautobot.ipam.filter_mixins import PrefixFilter
46
+ from nautobot.tenancy.filter_mixins import TenancyModelFilterSetMixin
49
47
 
50
48
  __all__ = (
51
49
  "BaseFilterSet",
nautobot/apps/models.py CHANGED
@@ -43,11 +43,11 @@ from nautobot.extras.models import (
43
43
  CustomFieldModel,
44
44
  RelationshipModel,
45
45
  StatusField,
46
- StatusModel,
47
46
  )
48
47
  from nautobot.extras.models.mixins import (
49
48
  ApprovableModelMixin,
50
49
  ContactMixin,
50
+ DataComplianceModelMixin,
51
51
  DynamicGroupMixin,
52
52
  DynamicGroupsModelMixin,
53
53
  NotesMixin,
@@ -76,6 +76,7 @@ __all__ = (
76
76
  "ContentTypeRelatedQuerySet",
77
77
  "CustomFieldModel",
78
78
  "CustomValidator",
79
+ "DataComplianceModelMixin",
79
80
  "DataComplianceRule",
80
81
  "DynamicGroupMixin",
81
82
  "DynamicGroupsModelMixin",
@@ -97,7 +98,6 @@ __all__ = (
97
98
  "RestrictedQuerySet",
98
99
  "SavedViewMixin",
99
100
  "StatusField",
100
- "StatusModel",
101
101
  "TagsField",
102
102
  "TagsManager",
103
103
  "TreeManager",
nautobot/apps/ui.py CHANGED
@@ -12,7 +12,13 @@ from nautobot.core.ui.breadcrumbs import (
12
12
  ModelBreadcrumbItem,
13
13
  ViewNameBreadcrumbItem,
14
14
  )
15
- from nautobot.core.ui.choices import EChartsTypeChoices, LayoutChoices, SectionChoices
15
+ from nautobot.core.ui.choices import (
16
+ EChartsTypeChoices,
17
+ LayoutChoices,
18
+ NavigationIconChoices,
19
+ NavigationWeightChoices,
20
+ SectionChoices,
21
+ )
16
22
  from nautobot.core.ui.echarts import (
17
23
  EChartsBase,
18
24
  queryset_to_nested_dict_keys_as_series,
@@ -90,6 +96,8 @@ __all__ = (
90
96
  "NavMenuImportButton",
91
97
  "NavMenuItem",
92
98
  "NavMenuTab",
99
+ "NavigationIconChoices",
100
+ "NavigationWeightChoices",
93
101
  "ObjectDetailContent",
94
102
  "ObjectFieldsPanel",
95
103
  "ObjectTextPanel",
@@ -13,8 +13,9 @@ from nautobot.dcim.filters import (
13
13
  PathEndpointModelFilterSetMixin,
14
14
  )
15
15
  from nautobot.dcim.models import Location
16
- from nautobot.extras.filters import NautobotFilterSet, StatusModelFilterSetMixin
17
- from nautobot.tenancy.filters.mixins import TenancyModelFilterSetMixin
16
+ from nautobot.extras.filter_mixins import StatusModelFilterSetMixin
17
+ from nautobot.extras.filters import NautobotFilterSet
18
+ from nautobot.tenancy.filter_mixins import TenancyModelFilterSetMixin
18
19
 
19
20
  from .models import Circuit, CircuitTermination, CircuitType, Provider, ProviderNetwork
20
21
 
@@ -4,12 +4,13 @@ from nautobot.core.apps import (
4
4
  NavMenuItem,
5
5
  NavMenuTab,
6
6
  )
7
+ from nautobot.core.ui.choices import NavigationIconChoices, NavigationWeightChoices
7
8
 
8
9
  menu_items = (
9
10
  NavMenuTab(
10
11
  name="Circuits",
11
- icon="rotate-cw",
12
- weight=500,
12
+ icon=NavigationIconChoices.CIRCUITS,
13
+ weight=NavigationWeightChoices.CIRCUITS,
13
14
  groups=(
14
15
  NavMenuGroup(
15
16
  name="Circuits",
@@ -1,2 +1,2 @@
1
- {% extends 'circuits/circuit_retrieve.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
@@ -10,9 +10,9 @@
10
10
  {% render_field form.circuit_type %}
11
11
  {% render_field form.status %}
12
12
  {% render_field form.install_date %}
13
- <div class="mb-10 d-flex justify-content-center">
14
- <label class="col-lg-3 col-form-label" for="id_commit_rate">{{ form.commit_rate.label }}</label>
15
- <div class="col-lg-9">
13
+ <div class="mb-10 d-md-flex justify-content-center">
14
+ <label class="col-md-3 col-form-label" for="id_commit_rate">{{ form.commit_rate.label }}</label>
15
+ <div class="col-md-9">
16
16
  <div class="input-group">
17
17
  {{ form.commit_rate }}
18
18
  {% include 'circuits/inc/speed_widget.html' with target_field='commit_rate' %}
@@ -1,2 +1,2 @@
1
- {% extends 'circuits/circuittermination_retrieve.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
@@ -7,24 +7,9 @@
7
7
  <div class="card">
8
8
  <div class="card-header"><strong>Termination</strong></div>
9
9
  <div class="card-body">
10
- <div class="mb-10 d-flex justify-content-center">
11
- <label class="col-lg-3 col-form-label">Provider</label>
12
- <div class="col-lg-9">
13
- <p class="form-control-plaintext">{{ obj.circuit.provider }}</p>
14
- </div>
15
- </div>
16
- <div class="mb-10 d-flex justify-content-center">
17
- <label class="col-lg-3 col-form-label">Circuit</label>
18
- <div class="col-lg-9">
19
- <p class="form-control-plaintext">{{ obj.circuit.cid }}</p>
20
- </div>
21
- </div>
22
- <div class="mb-10 d-flex justify-content-center">
23
- <label class="col-lg-3 col-form-label">Termination</label>
24
- <div class="col-lg-9">
25
- <p class="form-control-plaintext">{{ form.term_side.value }}</p>
26
- </div>
27
- </div>
10
+ {% include "inc/form_static_field.html" with label="Provider" value=obj.circuit.provider %}
11
+ {% include "inc/form_static_field.html" with label="Circuit" value=obj.circuit.cid %}
12
+ {% include "inc/form_static_field.html" with label="Termination" value=form.term_side.value %}
28
13
  {% with location_tab_active=form.initial.location %}
29
14
  {% with providernetwork_tab_active=form.initial.provider_network %}
30
15
  {% with cloudnetwork_tab_active=form.initial.cloud_network %}
@@ -58,9 +43,9 @@
58
43
  <div class="card">
59
44
  <div class="card-header"><strong>Termination Details</strong></div>
60
45
  <div class="card-body">
61
- <div class="mb-10 d-flex justify-content-center">
62
- <label class="col-lg-3 col-form-label" for="id_port_speed">{{ form.port_speed.label }}</label>
63
- <div class="col-lg-9">
46
+ <div class="mb-10 d-md-flex justify-content-center">
47
+ <label class="col-md-3 col-form-label" for="id_port_speed">{{ form.port_speed.label }}</label>
48
+ <div class="col-md-9">
64
49
  <div class="input-group">
65
50
  {{ form.port_speed }}
66
51
  {% include 'circuits/inc/speed_widget.html' with target_field='port_speed' %}
@@ -68,9 +53,9 @@
68
53
  <span class="form-text">{{ form.port_speed.help_text }}</span>
69
54
  </div>
70
55
  </div>
71
- <div class="mb-10 d-flex justify-content-center">
72
- <label class="col-lg-3 col-form-label" for="id_upstream_speed">{{ form.upstream_speed.label }}</label>
73
- <div class="col-lg-9">
56
+ <div class="mb-10 d-md-flex justify-content-center">
57
+ <label class="col-md-3 col-form-label" for="id_upstream_speed">{{ form.upstream_speed.label }}</label>
58
+ <div class="col-md-9">
74
59
  <div class="input-group">
75
60
  {{ form.upstream_speed }}
76
61
  {% include 'circuits/inc/speed_widget.html' with target_field='upstream_speed' %}
@@ -1,2 +1,2 @@
1
- {% extends 'circuits/circuittype_retrieve.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -24,14 +24,14 @@
24
24
  {% if perms.dcim.add_cable %}
25
25
  <div class="float-end">
26
26
  <span class="dropdown">
27
- <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
27
+ <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-bs-toggle="dropdown" data-bs-popper-config='{"strategy": "fixed"}' aria-haspopup="true" aria-expanded="false">
28
28
  <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect
29
29
  </button>
30
- <ul class="dropdown-menu dropdown-menu-right">
31
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='interface' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Interface</a></li>
32
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='front-port' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Front Port</a></li>
33
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='rear-port' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Rear Port</a></li>
34
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='circuit-termination' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Circuit Termination</a></li>
30
+ <ul class="dropdown-menu dropdown-menu-end">
31
+ <li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='interface' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Interface</a></li>
32
+ <li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='front-port' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Front Port</a></li>
33
+ <li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='rear-port' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Rear Port</a></li>
34
+ <li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' termination_a_id=termination.pk termination_b_type='circuit-termination' %}?termination_b_location={{ termination.location.pk }}&return_url={{ object.get_absolute_url }}">Circuit Termination</a></li>
35
35
  </ul>
36
36
  </span>
37
37
  </div>
@@ -1,17 +1,17 @@
1
1
  <span class="input-group-btn">
2
- <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown">
2
+ <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" data-bs-popper-config='{"strategy": "fixed"}'>
3
3
  <span class="mdi mdi-chevron-down"></span>
4
4
  </button>
5
- <ul class="dropdown-menu dropdown-menu-right">
6
- <li><a href="#" target="{{ target_field }}" data="10000" class="set_speed">10 Mbps</a></li>
7
- <li><a href="#" target="{{ target_field }}" data="100000" class="set_speed">100 Mbps</a></li>
8
- <li><a href="#" target="{{ target_field }}" data="1000000" class="set_speed">1 Gbps</a></li>
9
- <li><a href="#" target="{{ target_field }}" data="10000000" class="set_speed">10 Gbps</a></li>
10
- <li><a href="#" target="{{ target_field }}" data="25000000" class="set_speed">25 Gbps</a></li>
11
- <li><a href="#" target="{{ target_field }}" data="40000000" class="set_speed">40 Gbps</a></li>
12
- <li><a href="#" target="{{ target_field }}" data="100000000" class="set_speed">100 Gbps</a></li>
13
- <li class="divider"></li>
14
- <li><a href="#" target="{{ target_field }}" data="1544" class="set_speed">T1 (1.544 Mbps)</a></li>
15
- <li><a href="#" target="{{ target_field }}" data="2048" class="set_speed">E1 (2.048 Mbps)</a></li>
5
+ <ul class="dropdown-menu dropdown-menu-end">
6
+ <li><a href="#" target="{{ target_field }}" data="10000" class="set_speed dropdown-item">10 Mbps</a></li>
7
+ <li><a href="#" target="{{ target_field }}" data="100000" class="set_speed dropdown-item">100 Mbps</a></li>
8
+ <li><a href="#" target="{{ target_field }}" data="1000000" class="set_speed dropdown-item">1 Gbps</a></li>
9
+ <li><a href="#" target="{{ target_field }}" data="10000000" class="set_speed dropdown-item">10 Gbps</a></li>
10
+ <li><a href="#" target="{{ target_field }}" data="25000000" class="set_speed dropdown-item">25 Gbps</a></li>
11
+ <li><a href="#" target="{{ target_field }}" data="40000000" class="set_speed dropdown-item">40 Gbps</a></li>
12
+ <li><a href="#" target="{{ target_field }}" data="100000000" class="set_speed dropdown-item">100 Gbps</a></li>
13
+ <li><hr class="dropdown-divider"></li>
14
+ <li><a href="#" target="{{ target_field }}" data="1544" class="set_speed dropdown-item">T1 (1.544 Mbps)</a></li>
15
+ <li><a href="#" target="{{ target_field }}" data="2048" class="set_speed dropdown-item">E1 (2.048 Mbps)</a></li>
16
16
  </ul>
17
17
  </span>
@@ -1,2 +1,2 @@
1
- {% extends 'circuits/providernetwork_retrieve.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1,5 +1,4 @@
1
1
  from django.contrib.contenttypes.models import ContentType
2
- from django.test import tag
3
2
  from django.urls import reverse
4
3
 
5
4
  from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider
@@ -48,7 +47,6 @@ class CircuitTestCase(SeleniumTestCase, ObjectsListMixin, ObjectDetailsMixin):
48
47
  )
49
48
  return circuit
50
49
 
51
- @tag("fix_in_v3")
52
50
  def test_circuit_create(self):
53
51
  cid = "Circuit-test-abc123"
54
52
  description = "My Precious Circuit"
@@ -87,16 +85,15 @@ class CircuitTestCase(SeleniumTestCase, ObjectsListMixin, ObjectDetailsMixin):
87
85
  circuit = Circuit.objects.get(cid=cid)
88
86
  self.assertIn(self.live_server_url + reverse("circuits:circuit", kwargs={"pk": circuit.pk}), self.browser.url)
89
87
 
90
- self.assertPanelValue("Circuit", "Circuit ID", cid)
91
- self.assertPanelValue("Circuit", "Status", "Active")
92
- self.assertPanelValue("Circuit", "Provider", self.provider.name)
93
- self.assertPanelValue("Circuit", "Circuit Type", self.circuit_type.name)
94
- self.assertPanelValue("Circuit", "Tenant", self.tenant.name)
95
- self.assertPanelValue("Circuit", "Date Installed", "Jan. 1, 2025")
96
- self.assertPanelValue("Circuit", "Commit Rate (Kbps)", "192")
97
- self.assertPanelValue("Circuit", "Description", description)
88
+ self.assertPanelValue("CIRCUIT", "Circuit ID", cid)
89
+ self.assertPanelValue("CIRCUIT", "Status", "Active")
90
+ self.assertPanelValue("CIRCUIT", "Provider", self.provider.name)
91
+ self.assertPanelValue("CIRCUIT", "Circuit Type", self.circuit_type.name)
92
+ self.assertPanelValue("CIRCUIT", "Tenant", self.tenant.name)
93
+ self.assertPanelValue("CIRCUIT", "Date Installed", "Jan. 1, 2025")
94
+ self.assertPanelValue("CIRCUIT", "Commit Rate (Kbps)", "192")
95
+ self.assertPanelValue("CIRCUIT", "Description", description)
98
96
 
99
- @tag("fix_in_v3")
100
97
  def test_circuit_create_termination(self):
101
98
  circuit = self.create_circuit("Circuit-test-termination")
102
99
  sides = ["A", "Z"]
@@ -108,7 +105,7 @@ class CircuitTestCase(SeleniumTestCase, ObjectsListMixin, ObjectDetailsMixin):
108
105
  self.assertIn(details_url, self.browser.url)
109
106
 
110
107
  # Find and click add termination button
111
- termination_panel_xpath = f'//*[@id="main"]//div[@class="card-header"][contains(normalize-space(), "Termination - {side} Side")]'
108
+ termination_panel_xpath = f'//*[@id="main"]//div[contains(@class, "card-header") and contains(normalize-space(), "TERMINATION - {side} SIDE")]'
112
109
  self.browser.find_by_xpath(f'{termination_panel_xpath}//a[normalize-space()="Add"]').click()
113
110
 
114
111
  port_speed = ord(side)
@@ -129,7 +126,7 @@ class CircuitTestCase(SeleniumTestCase, ObjectsListMixin, ObjectDetailsMixin):
129
126
  self.assertTrue(self.browser.is_text_present(f"Created circuit termination Termination {side}"))
130
127
 
131
128
  # Assert that value are properly set
132
- panel_label = f"Termination - {side} Side"
129
+ panel_label = f"TERMINATION - {side} SIDE"
133
130
  self.assertPanelValue(panel_label, "Location", self.location.name)
134
131
  self.assertPanelValue(panel_label, "Speed", port_speed)
135
132
  self.assertPanelValue(panel_label, "Speed", upstream_speed)
nautobot/cloud/filters.py CHANGED
@@ -9,7 +9,7 @@ from nautobot.dcim.models import Manufacturer
9
9
  from nautobot.extras.filters import NautobotFilterSet
10
10
  from nautobot.extras.models import SecretsGroup
11
11
  from nautobot.extras.utils import FeatureQuery
12
- from nautobot.ipam.filters import PrefixFilter
12
+ from nautobot.ipam.filter_mixins import PrefixFilter
13
13
 
14
14
 
15
15
  class CloudAccountFilterSet(NautobotFilterSet):
@@ -4,12 +4,13 @@ from nautobot.core.apps import (
4
4
  NavMenuItem,
5
5
  NavMenuTab,
6
6
  )
7
+ from nautobot.core.ui.choices import NavigationIconChoices, NavigationWeightChoices
7
8
 
8
9
  menu_items = (
9
10
  NavMenuTab(
10
11
  name="Cloud",
11
- icon="cloud",
12
- weight=150,
12
+ icon=NavigationIconChoices.CLOUD,
13
+ weight=NavigationWeightChoices.CLOUD,
13
14
  groups=(
14
15
  NavMenuGroup(
15
16
  name="Cloud",
@@ -143,7 +143,7 @@ class NautobotAutoSchema(AutoSchema):
143
143
  "name": "exclude_m2m",
144
144
  "required": False,
145
145
  "description": "Exclude many-to-many fields from the response",
146
- "schema": {"type": "boolean", "default": False},
146
+ "schema": {"type": "boolean"},
147
147
  }
148
148
  )
149
149
  # TODO: add "include" parameter to the schema where relevant - nautobot/nautobot#685
@@ -64,6 +64,7 @@ class OptInFieldsMixin:
64
64
  - Removes all serializer fields specified in `Meta.opt_in_fields` list that aren't specified in the
65
65
  `include` query parameter. (applies to GET requests only)
66
66
  - If the `exclude_m2m` query parameter is truthy, remove all many-to-many serializer fields for performance.
67
+ If `exclude_m2m` is not provided, only `tags`, `content_types`, and `object_types` are included by default.
67
68
 
68
69
  As an example, if the serializer specifies that `opt_in_fields = ["computed_fields"]`
69
70
  but `computed_fields` is not specified in the `?include` query parameter, `computed_fields` will be popped
@@ -102,9 +103,13 @@ class OptInFieldsMixin:
102
103
 
103
104
  # If exclude_m2m is present and truthy, mark any many-to-many fields as write-only so they
104
105
  # don't get included in the response.
105
- if is_truthy(params.get("exclude_m2m", "false")):
106
+ # If exclude_m2m is not present, we include a subset of many-to-many fields by default.
107
+ exclude_m2m = params.get("exclude_m2m")
108
+ if exclude_m2m is None or is_truthy(exclude_m2m):
106
109
  for field_instance in fields.values():
107
110
  if isinstance(field_instance, (serializers.ManyRelatedField, serializers.ListSerializer)):
111
+ if exclude_m2m is None and field_instance.source in constants.DEFAULT_M2M_FIELDS:
112
+ continue
108
113
  field_instance.write_only = True
109
114
 
110
115
  self.__pruned_fields = fields
nautobot/core/api/urls.py CHANGED
@@ -51,6 +51,7 @@ urlpatterns = [
51
51
  path("tenancy/", include("nautobot.tenancy.api.urls")),
52
52
  path("users/", include("nautobot.users.api.urls")),
53
53
  path("virtualization/", include("nautobot.virtualization.api.urls")),
54
+ path("vpn/", include("nautobot.vpn.api.urls")),
54
55
  path("wireless/", include("nautobot.wireless.api.urls")),
55
56
  path("status/", StatusView.as_view(), name="api-status"),
56
57
  path("docs/", NautobotSpectacularSwaggerView.as_view(url_name="schema"), name="api_docs"),
@@ -479,6 +479,14 @@ class APIRootView(AuthenticatedAPIRootView):
479
479
  format=format,
480
480
  ),
481
481
  ),
482
+ (
483
+ "vpn",
484
+ reverse(
485
+ "vpn-api:api-root",
486
+ request=request,
487
+ format=format,
488
+ ),
489
+ ),
482
490
  (
483
491
  "wireless",
484
492
  reverse(
@@ -9,7 +9,6 @@ from django.db.models.signals import post_migrate
9
9
  from django.urls import reverse
10
10
  from django.urls.exceptions import NoReverseMatch
11
11
  from django.utils.http import urlencode
12
- from django.utils.module_loading import import_string
13
12
  from graphene.types import generic, String
14
13
 
15
14
  from nautobot.core.signals import nautobot_database_ready
@@ -31,7 +30,8 @@ from nautobot.core.ui.nav import ( # isort: skip # noqa: F401
31
30
  NavMenuTab,
32
31
  NAV_CONTEXT_NAMES,
33
32
  )
34
-
33
+ from nautobot.core.utils.module_loading import import_string_optional
34
+ from nautobot.core.utils.patch_social_django import patch_django_storage
35
35
  from nautobot.extras.registry import registry
36
36
 
37
37
  logger = logging.getLogger(__name__)
@@ -60,17 +60,11 @@ class NautobotConfig(AppConfig):
60
60
  """
61
61
  Ready function initiates the import application.
62
62
  """
63
- try:
64
- homepage_layout = import_string(f"{self.name}.{self.homepage_layout}")
63
+ if homepage_layout := import_string_optional(f"{self.name}.{self.homepage_layout}"):
65
64
  register_homepage_panels(self.path, self.label, homepage_layout)
66
- except ImportError:
67
- pass
68
65
 
69
- try:
70
- menu_items = import_string(f"{self.name}.{self.menu_tabs}")
66
+ if menu_items := import_string_optional(f"{self.name}.{self.menu_tabs}"):
71
67
  register_menu_items(menu_items)
72
- except ImportError:
73
- pass
74
68
 
75
69
 
76
70
  def create_or_check_entry(grouping, record, key, path):
@@ -364,6 +358,13 @@ class CoreConfig(NautobotConfig):
364
358
  logger.warning("Maintenance mode enabled: disabling update of most recent login time")
365
359
  user_logged_in.disconnect(update_last_login, dispatch_uid="update_last_login")
366
360
 
361
+ # SECURITY
362
+ # Patch social_django to prevent account takeover vulnerability
363
+ from social_django.models import DjangoStorage
364
+
365
+ # TODO: When upgrading to 5.6.0 or later, remove the patch
366
+ patch_django_storage(DjangoStorage)
367
+
367
368
  post_migrate.connect(post_migrate_send_nautobot_database_ready, sender=self)
368
369
 
369
370
  super().ready()
@@ -12,7 +12,6 @@ from django.apps import apps
12
12
  from django.conf import settings
13
13
  from django.db.utils import OperationalError, ProgrammingError
14
14
  from django.utils.functional import SimpleLazyObject
15
- from django.utils.module_loading import import_string
16
15
  from kombu.serialization import register
17
16
  from prometheus_client import CollectorRegistry, multiprocess, start_http_server
18
17
 
@@ -21,8 +20,7 @@ from nautobot.core.branching import BranchContext
21
20
  from nautobot.core.celery.control import discard_git_repository, refresh_git_repository # noqa: F401 # unused-import
22
21
  from nautobot.core.celery.encoders import NautobotKombuJSONEncoder
23
22
  from nautobot.core.celery.log import NautobotDatabaseHandler
24
- from nautobot.core.utils.module_loading import import_modules_privately
25
- from nautobot.extras.plugins.utils import import_object
23
+ from nautobot.core.utils.module_loading import import_modules_privately, import_string_optional
26
24
  from nautobot.extras.registry import registry
27
25
 
28
26
  logger = logging.getLogger(__name__)
@@ -140,7 +138,7 @@ def _import_dynamic_jobs_from_apps():
140
138
  del sys.modules[job.__module__]
141
139
 
142
140
  # Load app jobs
143
- app_config.features["jobs"] = import_object(f"{app_config.__module__}.{app_config.jobs}")
141
+ app_config.features["jobs"] = import_string_optional(f"{app_config.__module__}.{app_config.jobs}")
144
142
 
145
143
 
146
144
  def add_nautobot_log_handler(logger_instance, log_format=None):
@@ -220,7 +218,7 @@ def nautobot_kombu_json_loads_hook(data):
220
218
  qual_name = data.pop("__nautobot_type__")
221
219
  branch_name = data.pop("__nautobot_branch__", None)
222
220
  logger.debug("Performing nautobot deserialization for type %s", qual_name)
223
- cls = import_string(qual_name) # fully qualified dotted import path
221
+ cls = import_string_optional(qual_name) # fully qualified dotted import path
224
222
  if cls:
225
223
 
226
224
  def get_object():
nautobot/core/checks.py CHANGED
@@ -6,6 +6,9 @@ from django.core.exceptions import ValidationError
6
6
  from django.core.validators import URLValidator
7
7
  from django.db import connections
8
8
 
9
+ from nautobot.core.utils.config import get_settings_or_config
10
+ from nautobot.dcim.choices import DeviceUniquenessChoices
11
+
9
12
  E002 = Error(
10
13
  "'nautobot.core.authentication.ObjectPermissionBackend' must be included in AUTHENTICATION_BACKENDS",
11
14
  id="nautobot.core.E002",
@@ -42,6 +45,19 @@ W005 = Warning(
42
45
  obj=settings,
43
46
  )
44
47
 
48
+ W006 = Warning(
49
+ "The deprecated setting DEVICE_NAME_AS_NATURAL_KEY is still defined.",
50
+ hint="This setting has been superseded by DEVICE_UNIQUENESS (see Device Constraints).",
51
+ id="nautobot.core.W006",
52
+ obj=settings,
53
+ )
54
+
55
+ W007 = Warning(
56
+ "Invalid DEVICE_UNIQUENESS configuration value.",
57
+ hint=f"DEVICE_UNIQUENESS must be one of: {', '.join(DeviceUniquenessChoices.values())}.",
58
+ id="nautobot.core.W007",
59
+ )
60
+
45
61
  MIN_POSTGRESQL_MAJOR_VERSION = 12
46
62
  MIN_POSTGRESQL_MINOR_VERSION = 0
47
63
 
@@ -153,3 +169,33 @@ def check_data_validation_engine_installed(app_configs, **kwargs):
153
169
  if app_name in settings.PLUGINS or app_name in settings.PLUGINS_CONFIG:
154
170
  return [E006]
155
171
  return []
172
+
173
+
174
+ @register(Tags.compatibility)
175
+ def check_deprecated_device_name_as_natural_key(app_configs, **kwargs):
176
+ """
177
+ Warn if the deprecated DEVICE_NAME_AS_NATURAL_KEY setting is still defined.
178
+
179
+ This setting existed prior to 3.0 and has been replaced by the
180
+ DEVICE_UNIQUENESS Constance configuration.
181
+ """
182
+ try:
183
+ get_settings_or_config("DEVICE_NAME_AS_NATURAL_KEY")
184
+ return [W006]
185
+ except AttributeError:
186
+ pass
187
+ return []
188
+
189
+
190
+ @register(Tags.compatibility)
191
+ def check_valid_value_for_device_uniqueness(app_configs, **kwargs):
192
+ """
193
+ Warn if the invalid value for DEVICE_UNIQUENESS is set.
194
+ """
195
+ try:
196
+ device_uniqueness = get_settings_or_config("DEVICE_UNIQUENESS")
197
+ if device_uniqueness not in DeviceUniquenessChoices.values():
198
+ return [W007]
199
+ except AttributeError:
200
+ return [W007]
201
+ return []