nautobot 2.4.21__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 (919) hide show
  1. nautobot/apps/choices.py +2 -2
  2. nautobot/apps/filters.py +9 -9
  3. nautobot/apps/forms.py +2 -0
  4. nautobot/apps/models.py +7 -2
  5. nautobot/apps/ui.py +20 -1
  6. nautobot/apps/utils.py +2 -3
  7. nautobot/apps/views.py +7 -1
  8. nautobot/circuits/filters.py +8 -23
  9. nautobot/circuits/navigation.py +3 -1
  10. nautobot/circuits/templates/circuits/circuit_create.html +9 -9
  11. nautobot/circuits/templates/circuits/circuit_terminations_swap.html +2 -2
  12. nautobot/circuits/templates/circuits/circuittermination_create.html +24 -33
  13. nautobot/circuits/templates/circuits/inc/circuit_termination.html +10 -10
  14. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +13 -13
  15. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +6 -6
  16. nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +3 -3
  17. nautobot/circuits/templates/circuits/inc/speed_widget.html +13 -13
  18. nautobot/circuits/templates/circuits/provider_create.html +9 -9
  19. nautobot/circuits/tests/integration/test_circuit.py +19 -19
  20. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +3 -0
  21. nautobot/circuits/tests/integration/test_relationships.py +4 -12
  22. nautobot/circuits/views.py +0 -2
  23. nautobot/cloud/filters.py +1 -13
  24. nautobot/cloud/navigation.py +3 -1
  25. nautobot/cloud/templates/cloud/cloudnetwork_update.html +9 -9
  26. nautobot/cloud/templates/cloud/cloudservice_update.html +6 -6
  27. nautobot/core/api/fields.py +30 -2
  28. nautobot/core/api/schema.py +1 -1
  29. nautobot/core/api/serializers.py +9 -2
  30. nautobot/core/api/urls.py +2 -0
  31. nautobot/core/api/views.py +58 -37
  32. nautobot/core/apps/__init__.py +6 -12
  33. nautobot/core/branching.py +83 -0
  34. nautobot/core/celery/__init__.py +11 -6
  35. nautobot/core/celery/backends.py +2 -0
  36. nautobot/core/celery/encoders.py +7 -0
  37. nautobot/core/celery/task.py +44 -0
  38. nautobot/core/checks.py +60 -0
  39. nautobot/core/cli/bootstrap_v3_to_v5.py +776 -0
  40. nautobot/core/constants.py +9 -0
  41. nautobot/core/context_processors.py +84 -0
  42. nautobot/core/filters.py +131 -2
  43. nautobot/core/forms/__init__.py +4 -2
  44. nautobot/core/forms/fields.py +10 -8
  45. nautobot/core/forms/forms.py +21 -9
  46. nautobot/core/forms/search.py +0 -15
  47. nautobot/core/forms/widgets.py +3 -2
  48. nautobot/core/graphql/__init__.py +8 -26
  49. nautobot/core/graphql/generators.py +16 -6
  50. nautobot/core/graphql/schema.py +1 -1
  51. nautobot/core/graphql/schema_init.py +1 -2
  52. nautobot/core/graphql/utils.py +7 -9
  53. nautobot/core/jobs/__init__.py +158 -0
  54. nautobot/core/management/commands/generate_test_data.py +28 -9
  55. nautobot/core/models/__init__.py +17 -2
  56. nautobot/core/models/fields.py +3 -2
  57. nautobot/core/models/generics.py +9 -1
  58. nautobot/core/models/name_color_content_types.py +1 -1
  59. nautobot/core/models/ordering.py +7 -5
  60. nautobot/core/models/querysets.py +77 -2
  61. nautobot/core/models/tree_queries.py +6 -4
  62. nautobot/core/settings.py +30 -16
  63. nautobot/core/settings.yaml +13 -7
  64. nautobot/core/tables.py +114 -44
  65. nautobot/core/templates/403.html +1 -1
  66. nautobot/core/templates/403_csrf_failure.html +1 -1
  67. nautobot/core/templates/404.html +1 -1
  68. nautobot/core/templates/40x.html +8 -8
  69. nautobot/core/templates/500.html +10 -10
  70. nautobot/core/templates/about.html +13 -12
  71. nautobot/core/templates/admin/actions.html +1 -1
  72. nautobot/core/templates/admin/app_index.html +3 -3
  73. nautobot/core/templates/admin/base.html +45 -52
  74. nautobot/core/templates/admin/base_site.html +0 -9
  75. nautobot/core/templates/admin/change_form.html +5 -5
  76. nautobot/core/templates/admin/change_list.html +8 -12
  77. nautobot/core/templates/admin/change_list_results.html +3 -3
  78. nautobot/core/templates/admin/config/config.html +24 -24
  79. nautobot/core/templates/admin/delete_confirmation.html +5 -5
  80. nautobot/core/templates/admin/edit_inline/stacked.html +5 -5
  81. nautobot/core/templates/admin/edit_inline/tabular.html +3 -3
  82. nautobot/core/templates/admin/includes/fieldset.html +15 -15
  83. nautobot/core/templates/admin/index.html +8 -8
  84. nautobot/core/templates/admin/submit_line.html +5 -5
  85. nautobot/core/templates/base_django.html +36 -32
  86. nautobot/core/templates/buttons/add.html +1 -1
  87. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +2 -2
  88. nautobot/core/templates/buttons/export.html +17 -18
  89. nautobot/core/templates/buttons/job_import.html +2 -2
  90. nautobot/core/templates/components/breadcrumbs.html +19 -17
  91. nautobot/core/templates/components/button/dropdown.html +7 -5
  92. nautobot/core/templates/components/echarts.html +2 -0
  93. nautobot/core/templates/components/layout/one_over_two.html +3 -3
  94. nautobot/core/templates/components/layout/two_over_one.html +3 -3
  95. nautobot/core/templates/components/panel/body_content_data_table.html +2 -2
  96. nautobot/core/templates/components/panel/body_content_tags.html +1 -1
  97. nautobot/core/templates/components/panel/body_wrapper_generic.html +4 -2
  98. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  99. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +5 -3
  100. nautobot/core/templates/components/panel/body_wrapper_table.html +4 -2
  101. nautobot/core/templates/components/panel/footer_contacts_table.html +4 -4
  102. nautobot/core/templates/components/panel/footer_content_table.html +3 -3
  103. nautobot/core/templates/components/panel/grouping_toggle.html +12 -11
  104. nautobot/core/templates/components/panel/header_extra_content_table.html +2 -11
  105. nautobot/core/templates/components/panel/panel.html +6 -3
  106. nautobot/core/templates/components/panel/stats_panel_body.html +9 -7
  107. nautobot/core/templates/components/tab/content_wrapper.html +29 -1
  108. nautobot/core/templates/components/tab/label_wrapper.html +10 -2
  109. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +11 -4
  110. nautobot/core/templates/echarts/echarts.html +20 -0
  111. nautobot/core/templates/exceptions/import_error.html +2 -2
  112. nautobot/core/templates/exceptions/permission_error.html +1 -1
  113. nautobot/core/templates/exceptions/programming_error.html +2 -2
  114. nautobot/core/templates/generic/object_bulk_add_component.html +29 -20
  115. nautobot/core/templates/generic/object_bulk_create.html +87 -75
  116. nautobot/core/templates/generic/object_bulk_destroy.html +35 -37
  117. nautobot/core/templates/generic/object_bulk_remove.html +30 -26
  118. nautobot/core/templates/generic/object_bulk_rename.html +53 -40
  119. nautobot/core/templates/generic/object_bulk_update.html +36 -29
  120. nautobot/core/templates/generic/object_create.html +40 -27
  121. nautobot/core/templates/generic/object_import.html +36 -24
  122. nautobot/core/templates/generic/object_list.html +279 -215
  123. nautobot/core/templates/generic/object_notes.html +21 -11
  124. nautobot/core/templates/generic/object_retrieve.html +161 -213
  125. nautobot/core/templates/graphene/graphiql.html +113 -60
  126. nautobot/core/templates/home.html +164 -87
  127. nautobot/core/templates/import_success.html +3 -2
  128. nautobot/core/templates/inc/ajax_loader.html +1 -1
  129. nautobot/core/templates/inc/computed_fields/panel_data.html +25 -13
  130. nautobot/core/templates/inc/created_updated.html +12 -7
  131. nautobot/core/templates/inc/custom_fields/panel_data.html +28 -16
  132. nautobot/core/templates/inc/custom_fields_panel.html +3 -3
  133. nautobot/core/templates/inc/dynamic_groups_panel.html +3 -3
  134. nautobot/core/templates/inc/extras_features_edit_form_fields.html +15 -15
  135. nautobot/core/templates/inc/footer.html +90 -40
  136. nautobot/core/templates/inc/form_static_field.html +6 -0
  137. nautobot/core/templates/inc/header.html +75 -0
  138. nautobot/core/templates/inc/header_banners.html +17 -0
  139. nautobot/core/templates/inc/header_messages.html +6 -0
  140. nautobot/core/templates/inc/image_attachments.html +9 -9
  141. nautobot/core/templates/inc/javascript.html +7 -24
  142. nautobot/core/templates/inc/media.html +4 -29
  143. nautobot/core/templates/inc/modal.html +2 -2
  144. nautobot/core/templates/inc/nav_favorites.html +27 -0
  145. nautobot/core/templates/inc/nav_menu.html +150 -108
  146. nautobot/core/templates/inc/object_details_advanced_panel.html +84 -71
  147. nautobot/core/templates/inc/page_title.html +23 -0
  148. nautobot/core/templates/inc/paginator.html +39 -28
  149. nautobot/core/templates/inc/relationships/panel_override.html +3 -3
  150. nautobot/core/templates/inc/relationships_panel.html +3 -3
  151. nautobot/core/templates/inc/relationships_table_rows.html +1 -1
  152. nautobot/core/templates/inc/search_panel.html +22 -16
  153. nautobot/core/templates/inc/table.html +61 -36
  154. nautobot/core/templates/inc/tenancy_form_panel.html +3 -3
  155. nautobot/core/templates/login.html +17 -59
  156. nautobot/core/templates/modals/modal_theme.html +12 -23
  157. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  158. nautobot/core/templates/panel_table.html +8 -12
  159. nautobot/core/templates/redoc_ui.html +80 -0
  160. nautobot/core/templates/rest_framework/api.html +43 -21
  161. nautobot/core/templates/search.html +12 -13
  162. nautobot/core/templates/swagger_ui.html +19 -4
  163. nautobot/core/templates/system_jobs/import_objects.html +70 -58
  164. nautobot/core/templates/template.css +0 -6
  165. nautobot/core/templates/utilities/comment_form.html +34 -0
  166. nautobot/core/templates/utilities/confirmation_form.html +17 -9
  167. nautobot/core/templates/utilities/obj_table.html +19 -11
  168. nautobot/core/templates/utilities/render_field.html +27 -21
  169. nautobot/core/templates/utilities/render_jinja2.html +22 -25
  170. nautobot/core/templates/utilities/templatetags/advanced_filter_indicator.html +8 -0
  171. nautobot/core/templates/utilities/templatetags/badge.html +1 -1
  172. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +2 -3
  173. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +482 -0
  174. nautobot/core/templates/utilities/templatetags/modal_form_as_dialog.html +14 -18
  175. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +11 -11
  176. nautobot/core/templates/utilities/templatetags/table_config_form.html +51 -24
  177. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  178. nautobot/core/templates/utilities/templatetags/utilization_graph.html +3 -3
  179. nautobot/core/templates/utilities/theme_preview.html +829 -566
  180. nautobot/core/templates/utilities/worker_status.html +42 -41
  181. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  182. nautobot/core/templates/widgets/sluginput.html +2 -2
  183. nautobot/core/templatetags/buttons.py +38 -40
  184. nautobot/core/templatetags/helpers.py +105 -28
  185. nautobot/core/templatetags/ui_framework.py +17 -0
  186. nautobot/core/testing/api.py +76 -12
  187. nautobot/core/testing/filters.py +11 -27
  188. nautobot/core/testing/integration.py +128 -10
  189. nautobot/core/testing/mixins.py +7 -4
  190. nautobot/core/testing/utils.py +28 -5
  191. nautobot/core/testing/views.py +125 -27
  192. nautobot/core/tests/integration/test_app_home.py +39 -35
  193. nautobot/core/tests/integration/test_app_navbar.py +60 -67
  194. nautobot/core/tests/integration/test_filters.py +123 -55
  195. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  196. nautobot/core/tests/integration/test_home.py +10 -18
  197. nautobot/core/tests/integration/test_import_objects_ui.py +2 -9
  198. nautobot/core/tests/integration/test_navbar.py +41 -16
  199. nautobot/core/tests/integration/test_swagger.py +1 -7
  200. nautobot/core/tests/integration/test_theme.py +3 -0
  201. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  202. nautobot/core/tests/runner.py +6 -1
  203. nautobot/core/tests/test_api.py +5 -3
  204. nautobot/core/tests/test_branching.py +154 -0
  205. nautobot/core/tests/test_breadcrumbs.py +7 -8
  206. nautobot/core/tests/test_checks.py +28 -0
  207. nautobot/core/tests/test_commands.py +0 -41
  208. nautobot/core/tests/test_config.py +2 -1
  209. nautobot/core/tests/test_csv.py +4 -7
  210. nautobot/core/tests/test_filters.py +326 -318
  211. nautobot/core/tests/test_forms.py +19 -30
  212. nautobot/core/tests/test_graphql.py +67 -57
  213. nautobot/core/tests/test_models.py +1 -1
  214. nautobot/core/tests/test_nautobot_server.py +2 -0
  215. nautobot/core/tests/test_navigations.py +78 -10
  216. nautobot/core/tests/test_tables.py +3 -1
  217. nautobot/core/tests/test_templatetags_helpers.py +61 -21
  218. nautobot/core/tests/test_templatetags_ui_framework.py +36 -18
  219. nautobot/core/tests/test_ui.py +207 -2
  220. nautobot/core/tests/test_utils.py +147 -2
  221. nautobot/core/tests/test_views.py +201 -64
  222. nautobot/core/tests/test_views_utils.py +1 -1
  223. nautobot/core/ui/breadcrumbs.py +2 -12
  224. nautobot/core/ui/choices.py +190 -0
  225. nautobot/core/ui/constants.py +86 -0
  226. nautobot/core/ui/echarts.py +474 -0
  227. nautobot/core/ui/nav.py +5 -1
  228. nautobot/core/ui/object_detail.py +180 -16
  229. nautobot/core/urls.py +13 -1
  230. nautobot/core/utils/cache.py +71 -0
  231. nautobot/core/utils/data.py +8 -5
  232. nautobot/core/utils/filtering.py +8 -2
  233. nautobot/core/utils/git.py +3 -3
  234. nautobot/core/utils/lookup.py +87 -13
  235. nautobot/core/utils/migrations.py +22 -0
  236. nautobot/core/utils/module_loading.py +26 -0
  237. nautobot/core/utils/permissions.py +9 -5
  238. nautobot/core/views/__init__.py +114 -63
  239. nautobot/core/views/generic.py +34 -27
  240. nautobot/core/views/mixins.py +49 -27
  241. nautobot/core/views/renderers.py +3 -5
  242. nautobot/core/views/utils.py +10 -5
  243. nautobot/core/views/viewsets.py +2 -1
  244. nautobot/data_validation/__init__.py +0 -0
  245. nautobot/data_validation/api/__init__.py +1 -0
  246. nautobot/data_validation/api/serializers.py +80 -0
  247. nautobot/data_validation/api/urls.py +20 -0
  248. nautobot/data_validation/api/views.py +44 -0
  249. nautobot/data_validation/apps.py +18 -0
  250. nautobot/data_validation/custom_validators.py +330 -0
  251. nautobot/data_validation/filters.py +133 -0
  252. nautobot/data_validation/form_mixin.py +25 -0
  253. nautobot/data_validation/forms.py +342 -0
  254. nautobot/data_validation/migrations/0001_initial.py +224 -0
  255. nautobot/data_validation/migrations/0002_data_migration_from_app.py +324 -0
  256. nautobot/data_validation/migrations/__init__.py +0 -0
  257. nautobot/data_validation/models.py +361 -0
  258. nautobot/data_validation/navigation.py +74 -0
  259. nautobot/data_validation/signals.py +30 -0
  260. nautobot/data_validation/tables.py +259 -0
  261. nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +1 -0
  262. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +11 -0
  263. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  264. nautobot/data_validation/tests/__init__.py +20 -0
  265. nautobot/data_validation/tests/migrations/__init__.py +0 -0
  266. nautobot/data_validation/tests/migrations/test_migrations.py +489 -0
  267. nautobot/data_validation/tests/test_api.py +238 -0
  268. nautobot/data_validation/tests/test_custom_validators.py +423 -0
  269. nautobot/data_validation/tests/test_data_compliance_rules.py +85 -0
  270. nautobot/data_validation/tests/test_filters.py +240 -0
  271. nautobot/data_validation/tests/test_form_mixin.py +115 -0
  272. nautobot/data_validation/tests/test_models.py +393 -0
  273. nautobot/data_validation/tests/test_views.py +435 -0
  274. nautobot/data_validation/urls.py +21 -0
  275. nautobot/data_validation/views.py +227 -0
  276. nautobot/dcim/api/serializers.py +10 -13
  277. nautobot/dcim/api/urls.py +2 -0
  278. nautobot/dcim/api/views.py +7 -0
  279. nautobot/dcim/apps.py +4 -0
  280. nautobot/dcim/choices.py +16 -0
  281. nautobot/dcim/custom_validators.py +84 -0
  282. nautobot/dcim/filter_mixins.py +353 -4
  283. nautobot/dcim/{filters/__init__.py → filters.py} +70 -157
  284. nautobot/dcim/forms.py +12 -6
  285. nautobot/dcim/graphql/types.py +1 -0
  286. nautobot/dcim/migrations/0075_add_deviceclusterassignment.py +52 -0
  287. nautobot/dcim/migrations/0076_device_cluster_to_clusters_data_migration.py +40 -0
  288. nautobot/dcim/migrations/0077_remove_device_cluster.py +14 -0
  289. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  290. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  291. nautobot/dcim/models/__init__.py +2 -0
  292. nautobot/dcim/models/device_components.py +3 -1
  293. nautobot/dcim/models/devices.py +115 -51
  294. nautobot/dcim/navigation.py +7 -3
  295. nautobot/dcim/querysets.py +6 -0
  296. nautobot/dcim/signals.py +19 -0
  297. nautobot/dcim/tables/devices.py +9 -5
  298. nautobot/dcim/tables/template_code.py +191 -102
  299. nautobot/dcim/templates/dcim/cable.html +1 -1
  300. nautobot/dcim/templates/dcim/cable_connect.html +62 -146
  301. nautobot/dcim/templates/dcim/cable_retrieve.html +10 -10
  302. nautobot/dcim/templates/dcim/cable_trace.html +15 -17
  303. nautobot/dcim/templates/dcim/console_port_connection_list.html +2 -2
  304. nautobot/dcim/templates/dcim/consoleport.html +18 -17
  305. nautobot/dcim/templates/dcim/consoleserverport.html +18 -17
  306. nautobot/dcim/templates/dcim/controller_create.html +12 -8
  307. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +1 -1
  308. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +6 -6
  309. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -1
  310. nautobot/dcim/templates/dcim/device/config.html +17 -19
  311. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +4 -4
  312. nautobot/dcim/templates/dcim/device/status.html +20 -20
  313. nautobot/dcim/templates/dcim/device_component_add.html +24 -15
  314. nautobot/dcim/templates/dcim/device_create.html +120 -120
  315. nautobot/dcim/templates/dcim/device_list.html +75 -12
  316. nautobot/dcim/templates/dcim/devicebay.html +7 -7
  317. nautobot/dcim/templates/dcim/devicebay_populate.html +29 -23
  318. nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +6 -6
  319. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  320. nautobot/dcim/templates/dcim/devicetype_component_add.html +25 -19
  321. nautobot/dcim/templates/dcim/devicetype_list.html +4 -4
  322. nautobot/dcim/templates/dcim/devicetype_update.html +9 -9
  323. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +3 -3
  324. nautobot/dcim/templates/dcim/frontport.html +21 -20
  325. nautobot/dcim/templates/dcim/inc/cable_form.html +7 -7
  326. nautobot/dcim/templates/dcim/inc/cable_termination.html +1 -1
  327. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +18 -9
  328. nautobot/dcim/templates/dcim/inc/detail_softwareversion_softwareimagefile_rows.html +1 -1
  329. nautobot/dcim/templates/dcim/inc/device_interface_filter.html +1 -1
  330. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +10 -10
  331. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  332. nautobot/dcim/templates/dcim/inc/homepage_connections.html +2 -2
  333. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +10 -10
  334. nautobot/dcim/templates/dcim/inc/rack_elevation.html +2 -2
  335. nautobot/dcim/templates/dcim/interface.html +42 -22
  336. nautobot/dcim/templates/dcim/interface_connection_list.html +2 -2
  337. nautobot/dcim/templates/dcim/interface_edit.html +26 -11
  338. nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +3 -3
  339. nautobot/dcim/templates/dcim/inventoryitem.html +3 -3
  340. nautobot/dcim/templates/dcim/inventoryitem_add.html +21 -10
  341. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  342. nautobot/dcim/templates/dcim/inventoryitem_edit.html +6 -4
  343. nautobot/dcim/templates/dcim/location.html +1 -1
  344. nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +24 -18
  345. nautobot/dcim/templates/dcim/location_retrieve.html +1 -1
  346. nautobot/dcim/templates/dcim/location_update.html +9 -9
  347. nautobot/dcim/templates/dcim/locationtype.html +0 -1
  348. nautobot/dcim/templates/dcim/module/base.html +67 -27
  349. nautobot/dcim/templates/dcim/module_consoleports.html +13 -15
  350. nautobot/dcim/templates/dcim/module_consoleserverports.html +13 -15
  351. nautobot/dcim/templates/dcim/module_frontports.html +13 -15
  352. nautobot/dcim/templates/dcim/module_interfaces.html +14 -16
  353. nautobot/dcim/templates/dcim/module_list.html +59 -10
  354. nautobot/dcim/templates/dcim/module_modulebays.html +12 -14
  355. nautobot/dcim/templates/dcim/module_poweroutlets.html +13 -15
  356. nautobot/dcim/templates/dcim/module_powerports.html +13 -15
  357. nautobot/dcim/templates/dcim/module_rearports.html +13 -15
  358. nautobot/dcim/templates/dcim/module_retrieve.html +3 -3
  359. nautobot/dcim/templates/dcim/module_update.html +15 -9
  360. nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -93
  361. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +7 -7
  362. nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
  363. nautobot/dcim/templates/dcim/moduletype_retrieve.html +74 -35
  364. nautobot/dcim/templates/dcim/platform_create.html +9 -9
  365. nautobot/dcim/templates/dcim/power_port_connection_list.html +3 -3
  366. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  367. nautobot/dcim/templates/dcim/powerfeed_edit.html +15 -15
  368. nautobot/dcim/templates/dcim/poweroutlet.html +13 -13
  369. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  370. nautobot/dcim/templates/dcim/powerport.html +17 -16
  371. nautobot/dcim/templates/dcim/rack.html +1 -1
  372. nautobot/dcim/templates/dcim/rack_elevation.html +2 -2
  373. nautobot/dcim/templates/dcim/rack_elevation_list.html +21 -9
  374. nautobot/dcim/templates/dcim/rack_retrieve.html +75 -57
  375. nautobot/dcim/templates/dcim/rack_update.html +14 -14
  376. nautobot/dcim/templates/dcim/rackreservation.html +1 -1
  377. nautobot/dcim/templates/dcim/rackreservation_edit.html +6 -6
  378. nautobot/dcim/templates/dcim/rearport.html +19 -18
  379. nautobot/dcim/templates/dcim/trace/cable.html +1 -1
  380. nautobot/dcim/templates/dcim/trace/circuit.html +1 -1
  381. nautobot/dcim/templates/dcim/trace/device.html +1 -1
  382. nautobot/dcim/templates/dcim/trace/powerpanel.html +1 -1
  383. nautobot/dcim/templates/dcim/trace/termination.html +1 -1
  384. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  385. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +25 -16
  386. nautobot/dcim/templates/dcim/virtualchassis_create.html +6 -6
  387. nautobot/dcim/templates/dcim/virtualchassis_edit.html +1 -1
  388. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -1
  389. nautobot/dcim/templates/dcim/virtualchassis_update.html +36 -22
  390. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +9 -9
  391. nautobot/dcim/tests/integration/test_controller.py +6 -6
  392. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +7 -7
  393. nautobot/dcim/tests/integration/test_create_device.py +9 -9
  394. nautobot/dcim/tests/integration/test_device_bulk_operations.py +7 -2
  395. nautobot/dcim/tests/integration/test_fileinputpicker.py +5 -7
  396. nautobot/dcim/tests/integration/test_location_bulk_operations.py +2 -0
  397. nautobot/dcim/tests/integration/test_module_bay_position.py +4 -1
  398. nautobot/dcim/tests/test_api.py +86 -6
  399. nautobot/dcim/tests/test_custom_validators.py +229 -0
  400. nautobot/dcim/tests/test_filters.py +159 -110
  401. nautobot/dcim/tests/test_graphql.py +32 -36
  402. nautobot/dcim/tests/test_jobs.py +1 -1
  403. nautobot/dcim/tests/test_models.py +229 -1
  404. nautobot/dcim/tests/test_views.py +31 -20
  405. nautobot/dcim/utils.py +3 -3
  406. nautobot/dcim/views.py +77 -41
  407. nautobot/extras/api/serializers.py +83 -19
  408. nautobot/extras/api/urls.py +7 -0
  409. nautobot/extras/api/views.py +243 -140
  410. nautobot/extras/choices.py +34 -13
  411. nautobot/extras/constants.py +1 -1
  412. nautobot/extras/context_managers.py +26 -26
  413. nautobot/extras/datasources/git.py +22 -0
  414. nautobot/extras/datasources/registry.py +3 -0
  415. nautobot/extras/exceptions.py +5 -0
  416. nautobot/extras/factory.py +11 -1
  417. nautobot/extras/{filters/mixins.py → filter_mixins.py} +4 -3
  418. nautobot/extras/{filters/__init__.py → filters.py} +203 -58
  419. nautobot/extras/forms/base.py +2 -1
  420. nautobot/extras/forms/forms.py +225 -20
  421. nautobot/extras/forms/mixins.py +0 -41
  422. nautobot/extras/homepage.py +21 -2
  423. nautobot/extras/jobs.py +2 -8
  424. nautobot/extras/jobs_ui.py +2 -2
  425. nautobot/extras/management/__init__.py +9 -0
  426. nautobot/extras/managers.py +31 -22
  427. nautobot/extras/migrations/0126_approval_workflow_pre_check.py +58 -0
  428. nautobot/extras/migrations/0127_approval_workflow_models.py +266 -0
  429. nautobot/extras/migrations/0128_remove_job_approval_required_and_more.py +29 -0
  430. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  431. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  432. nautobot/extras/models/__init__.py +14 -3
  433. nautobot/extras/models/approvals.py +556 -0
  434. nautobot/extras/models/change_logging.py +1 -0
  435. nautobot/extras/models/contacts.py +2 -0
  436. nautobot/extras/models/customfields.py +57 -22
  437. nautobot/extras/models/datasources.py +21 -0
  438. nautobot/extras/models/groups.py +2 -0
  439. nautobot/extras/models/jobs.py +122 -39
  440. nautobot/extras/models/metadata.py +2 -3
  441. nautobot/extras/models/mixins.py +129 -1
  442. nautobot/extras/models/models.py +22 -14
  443. nautobot/extras/models/relationships.py +47 -10
  444. nautobot/extras/models/secrets.py +1 -0
  445. nautobot/extras/models/statuses.py +0 -15
  446. nautobot/extras/models/tags.py +1 -1
  447. nautobot/extras/navigation.py +42 -15
  448. nautobot/extras/plugins/__init__.py +33 -55
  449. nautobot/extras/plugins/marketplace_manifest.yml +1 -23
  450. nautobot/extras/plugins/tables.py +8 -6
  451. nautobot/extras/plugins/urls.py +2 -21
  452. nautobot/extras/plugins/utils.py +1 -33
  453. nautobot/extras/plugins/validators.py +10 -10
  454. nautobot/extras/plugins/views.py +1 -5
  455. nautobot/extras/querysets.py +17 -21
  456. nautobot/extras/signals.py +23 -8
  457. nautobot/extras/tables.py +420 -99
  458. nautobot/extras/templates/extras/approval_dashboard.html +15 -0
  459. nautobot/extras/templates/extras/approval_workflow/approve.html +11 -0
  460. nautobot/extras/templates/extras/approval_workflow/comment.html +9 -0
  461. nautobot/extras/templates/extras/approval_workflow/deny.html +10 -0
  462. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +77 -0
  463. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +29 -0
  464. nautobot/extras/templates/extras/configcontext_update.html +12 -12
  465. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  466. nautobot/extras/templates/extras/configcontextschema_retrieve.html +9 -9
  467. nautobot/extras/templates/extras/configcontextschema_update.html +6 -6
  468. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  469. nautobot/extras/templates/extras/customfield_update.html +12 -12
  470. nautobot/extras/templates/extras/dynamicgroup.html +1 -1
  471. nautobot/extras/templates/extras/dynamicgroup_edit.html +1 -1
  472. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +17 -17
  473. nautobot/extras/templates/extras/dynamicgroup_update.html +24 -24
  474. nautobot/extras/templates/extras/externalintegration_update.html +6 -6
  475. nautobot/extras/templates/extras/gitrepository.html +1 -1
  476. nautobot/extras/templates/extras/gitrepository_object_edit.html +1 -1
  477. nautobot/extras/templates/extras/gitrepository_result.html +1 -1
  478. nautobot/extras/templates/extras/gitrepository_retrieve.html +12 -12
  479. nautobot/extras/templates/extras/gitrepository_update.html +25 -7
  480. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -1
  481. nautobot/extras/templates/extras/inc/approval_buttons_column.html +38 -0
  482. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +14 -13
  483. nautobot/extras/templates/extras/inc/configcontext_format.html +11 -4
  484. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +7 -7
  485. nautobot/extras/templates/extras/inc/job_label.html +5 -5
  486. nautobot/extras/templates/extras/inc/job_table.html +23 -10
  487. nautobot/extras/templates/extras/inc/job_tiles.html +33 -21
  488. nautobot/extras/templates/extras/inc/jobresult.html +6 -6
  489. nautobot/extras/templates/extras/inc/json_format.html +11 -4
  490. nautobot/extras/templates/extras/inc/object_contact_header.html +6 -6
  491. nautobot/extras/templates/extras/inc/overridable_field.html +16 -15
  492. nautobot/extras/templates/extras/inc/panel_approvalworkflowstage.html +34 -0
  493. nautobot/extras/templates/extras/inc/panel_changelog.html +9 -9
  494. nautobot/extras/templates/extras/inc/panel_jobhistory.html +8 -6
  495. nautobot/extras/templates/extras/inc/tags_panel.html +3 -3
  496. nautobot/extras/templates/extras/job.html +154 -155
  497. nautobot/extras/templates/extras/job_approval_confirmation.html +4 -27
  498. nautobot/extras/templates/extras/job_bulk_edit.html +18 -1
  499. nautobot/extras/templates/extras/job_detail.html +1 -1
  500. nautobot/extras/templates/extras/job_edit.html +69 -64
  501. nautobot/extras/templates/extras/job_list.html +37 -60
  502. nautobot/extras/templates/extras/jobresult.html +1 -1
  503. nautobot/extras/templates/extras/jobresult_retrieve.html +17 -17
  504. nautobot/extras/templates/extras/marketplace.html +62 -71
  505. nautobot/extras/templates/extras/metadatatype_create.html +9 -9
  506. nautobot/extras/templates/extras/note.html +1 -1
  507. nautobot/extras/templates/extras/object_approvalworkflow.html +36 -0
  508. nautobot/extras/templates/extras/object_assign_contact_or_team.html +16 -7
  509. nautobot/extras/templates/extras/object_configcontext.html +20 -20
  510. nautobot/extras/templates/extras/object_new_contact.html +6 -6
  511. nautobot/extras/templates/extras/object_new_team.html +6 -6
  512. nautobot/extras/templates/extras/objectchange.html +1 -1
  513. nautobot/extras/templates/extras/objectchange_retrieve.html +37 -56
  514. nautobot/extras/templates/extras/plugin_detail.html +40 -41
  515. nautobot/extras/templates/extras/plugins_list.html +23 -38
  516. nautobot/extras/templates/extras/plugins_tiles.html +28 -28
  517. nautobot/extras/templates/extras/role_retrieve.html +112 -48
  518. nautobot/extras/templates/extras/scheduledjob.html +25 -28
  519. nautobot/extras/templates/extras/secret_create.html +11 -11
  520. nautobot/extras/templates/extras/secretsgroup_update.html +6 -6
  521. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +3 -3
  522. nautobot/extras/templates/extras/status.html +1 -1
  523. nautobot/extras/templates/extras/tag.html +1 -1
  524. nautobot/extras/templates/extras/tag_update.html +3 -3
  525. nautobot/extras/templates/extras/templatetags/log_level.html +1 -1
  526. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -2
  527. nautobot/extras/templates/extras/webhook.html +12 -12
  528. nautobot/extras/templatetags/approvals.py +19 -0
  529. nautobot/extras/templatetags/custom_links.py +12 -12
  530. nautobot/extras/templatetags/job_buttons.py +14 -12
  531. nautobot/extras/test_jobs/invalid_import.py +9 -0
  532. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  533. nautobot/extras/test_jobs/missing_import.py +11 -0
  534. nautobot/extras/tests/integration/test_computedfields.py +5 -8
  535. nautobot/extras/tests/integration/test_configcontextschema.py +43 -48
  536. nautobot/extras/tests/integration/test_customfields.py +33 -33
  537. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -10
  538. nautobot/extras/tests/integration/test_jobs.py +2 -4
  539. nautobot/extras/tests/integration/test_notes.py +3 -9
  540. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  541. nautobot/extras/tests/integration/test_plugins.py +35 -27
  542. nautobot/extras/tests/integration/test_relationships.py +7 -11
  543. nautobot/extras/tests/integration/test_tagfilter.py +3 -11
  544. nautobot/extras/tests/test_api.py +786 -242
  545. nautobot/extras/tests/test_approvals.py +715 -0
  546. nautobot/extras/tests/test_changelog.py +18 -14
  547. nautobot/extras/tests/test_customfields.py +14 -13
  548. nautobot/extras/tests/test_datasources.py +1 -1
  549. nautobot/extras/tests/test_dynamicgroups.py +9 -4
  550. nautobot/extras/tests/test_filters.py +443 -13
  551. nautobot/extras/tests/test_forms.py +18 -57
  552. nautobot/extras/tests/test_jobs.py +25 -4
  553. nautobot/extras/tests/test_migrations.py +81 -1
  554. nautobot/extras/tests/test_models.py +378 -47
  555. nautobot/extras/tests/test_plugins.py +47 -13
  556. nautobot/extras/tests/test_relationships.py +7 -2
  557. nautobot/extras/tests/test_utils.py +2 -0
  558. nautobot/extras/tests/test_views.py +780 -493
  559. nautobot/extras/urls.py +36 -12
  560. nautobot/extras/utils.py +58 -12
  561. nautobot/extras/views.py +668 -209
  562. nautobot/ipam/factory.py +7 -0
  563. nautobot/ipam/filter_mixins.py +38 -0
  564. nautobot/ipam/filters.py +35 -71
  565. nautobot/ipam/formfields.py +1 -1
  566. nautobot/ipam/forms.py +6 -3
  567. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  568. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  569. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  570. nautobot/ipam/models.py +29 -2
  571. nautobot/ipam/navigation.py +3 -1
  572. nautobot/ipam/querysets.py +1 -2
  573. nautobot/ipam/tables.py +26 -17
  574. nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +6 -6
  575. nautobot/ipam/templates/ipam/inc/service.html +8 -8
  576. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  577. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +3 -2
  578. nautobot/ipam/templates/ipam/ipaddress.html +27 -13
  579. nautobot/ipam/templates/ipam/ipaddress_assign.html +31 -24
  580. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +3 -3
  581. nautobot/ipam/templates/ipam/ipaddress_edit.html +9 -9
  582. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +7 -9
  583. nautobot/ipam/templates/ipam/ipaddress_merge.html +195 -186
  584. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +7 -9
  585. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +7 -5
  586. nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
  587. nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
  588. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  589. nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
  590. nautobot/ipam/templates/ipam/prefix_create.html +9 -9
  591. nautobot/ipam/templates/ipam/prefix_list.html +15 -14
  592. nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -1
  593. nautobot/ipam/templates/ipam/vlan.html +1 -1
  594. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  595. nautobot/ipam/templates/ipam/vlan_update.html +6 -6
  596. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  597. nautobot/ipam/templates/ipam/vrf_edit.html +15 -15
  598. nautobot/ipam/tests/integration/test_prefixes.py +5 -13
  599. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  600. nautobot/ipam/tests/test_api.py +20 -7
  601. nautobot/ipam/tests/test_filters.py +10 -0
  602. nautobot/ipam/tests/test_forms.py +1 -1
  603. nautobot/ipam/tests/test_models.py +1 -1
  604. nautobot/ipam/tests/test_tables.py +1 -2
  605. nautobot/ipam/tests/test_utils.py +1 -1
  606. nautobot/ipam/tests/test_views.py +24 -21
  607. nautobot/ipam/ui.py +0 -17
  608. nautobot/ipam/utils/migrations.py +16 -2
  609. nautobot/ipam/utils/testing.py +9 -3
  610. nautobot/ipam/views.py +49 -7
  611. nautobot/project-static/dist/css/graphql-libraries.css +655 -0
  612. nautobot/project-static/dist/css/graphql-libraries.css.map +1 -0
  613. nautobot/project-static/dist/css/materialdesignicons.css +3 -0
  614. nautobot/project-static/dist/css/materialdesignicons.css.map +1 -0
  615. nautobot/project-static/dist/css/nautobot.css +13 -0
  616. nautobot/project-static/dist/css/nautobot.css.map +1 -0
  617. nautobot/project-static/dist/js/graphql-libraries.js +3 -0
  618. nautobot/project-static/dist/js/graphql-libraries.js.LICENSE.txt +62 -0
  619. nautobot/project-static/dist/js/graphql-libraries.js.map +1 -0
  620. nautobot/project-static/dist/js/libraries.js +3 -0
  621. nautobot/project-static/dist/js/libraries.js.LICENSE.txt +65 -0
  622. nautobot/project-static/dist/js/libraries.js.map +1 -0
  623. nautobot/project-static/dist/js/materialdesignicons.js +0 -0
  624. nautobot/project-static/dist/js/nautobot-graphiql.js +2 -0
  625. nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -0
  626. nautobot/project-static/dist/js/nautobot.js +2 -0
  627. nautobot/project-static/dist/js/nautobot.js.map +1 -0
  628. nautobot/project-static/fonts/Montserrat-v30-Bold.woff2 +0 -0
  629. nautobot/project-static/fonts/Montserrat-v30-Light.woff2 +0 -0
  630. nautobot/project-static/fonts/Montserrat-v30-Regular.woff2 +0 -0
  631. nautobot/project-static/fonts/Roboto-v48-Bold.woff2 +0 -0
  632. nautobot/project-static/fonts/Roboto-v48-Light.woff2 +0 -0
  633. nautobot/project-static/fonts/Roboto-v48-Regular.woff2 +0 -0
  634. nautobot/project-static/img/jinja_logo.svg +21 -92
  635. nautobot/project-static/js/cabletrace.js +1 -1
  636. nautobot/project-static/js/editor.js +4 -4
  637. nautobot/project-static/js/forms.js +67 -717
  638. nautobot/project-static/js/job_result.js +2 -2
  639. nautobot/project-static/nautobot-icons/360-degrees.svg +3 -0
  640. nautobot/project-static/nautobot-icons/arrow-decision.svg +3 -0
  641. nautobot/project-static/nautobot-icons/arrows-expand-rec.svg +3 -0
  642. nautobot/project-static/nautobot-icons/arrows-move-2-rec.svg +3 -0
  643. nautobot/project-static/nautobot-icons/arrows-move-rec.svg +3 -0
  644. nautobot/project-static/nautobot-icons/atom.svg +3 -0
  645. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  646. nautobot/project-static/nautobot-icons/branch.svg +3 -0
  647. nautobot/project-static/nautobot-icons/briefcase-2.svg +3 -0
  648. nautobot/project-static/nautobot-icons/cable-data-2.svg +3 -0
  649. nautobot/project-static/nautobot-icons/cable-data.svg +3 -0
  650. nautobot/project-static/nautobot-icons/cast.svg +3 -0
  651. nautobot/project-static/nautobot-icons/check-circle.svg +3 -0
  652. nautobot/project-static/nautobot-icons/checkbox-circle.svg +3 -0
  653. nautobot/project-static/nautobot-icons/checkbox-rec.svg +3 -0
  654. nautobot/project-static/nautobot-icons/cloud-check.svg +3 -0
  655. nautobot/project-static/nautobot-icons/cloud-lightning.svg +3 -0
  656. nautobot/project-static/nautobot-icons/cloud-upload.svg +3 -0
  657. nautobot/project-static/nautobot-icons/cloud.svg +3 -0
  658. nautobot/project-static/nautobot-icons/compass.svg +3 -0
  659. nautobot/project-static/nautobot-icons/control-panel.svg +3 -0
  660. nautobot/project-static/nautobot-icons/credit-card.svg +3 -0
  661. nautobot/project-static/nautobot-icons/device-lifecycle.svg +3 -0
  662. nautobot/project-static/nautobot-icons/direction.svg +3 -0
  663. nautobot/project-static/nautobot-icons/elements.svg +3 -0
  664. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  665. nautobot/project-static/nautobot-icons/globe-2.svg +3 -0
  666. nautobot/project-static/nautobot-icons/globe.svg +3 -0
  667. nautobot/project-static/nautobot-icons/hammer.svg +3 -0
  668. nautobot/project-static/nautobot-icons/history.svg +3 -0
  669. nautobot/project-static/nautobot-icons/ip.svg +3 -0
  670. nautobot/project-static/nautobot-icons/laptop.svg +3 -0
  671. nautobot/project-static/nautobot-icons/lightning.svg +3 -0
  672. nautobot/project-static/nautobot-icons/list-unordered.svg +3 -0
  673. nautobot/project-static/nautobot-icons/map-view.svg +3 -0
  674. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  675. nautobot/project-static/nautobot-icons/pin-2.svg +3 -0
  676. nautobot/project-static/nautobot-icons/pin-3.svg +3 -0
  677. nautobot/project-static/nautobot-icons/plug.svg +3 -0
  678. nautobot/project-static/nautobot-icons/refresh-cw.svg +3 -0
  679. nautobot/project-static/nautobot-icons/rocket-2.svg +3 -0
  680. nautobot/project-static/nautobot-icons/rotate-cw.svg +3 -0
  681. nautobot/project-static/nautobot-icons/route.svg +3 -0
  682. nautobot/project-static/nautobot-icons/secrets.svg +3 -0
  683. nautobot/project-static/nautobot-icons/security.svg +3 -0
  684. nautobot/project-static/nautobot-icons/server-2.svg +3 -0
  685. nautobot/project-static/nautobot-icons/server.svg +3 -0
  686. nautobot/project-static/nautobot-icons/share.svg +3 -0
  687. nautobot/project-static/nautobot-icons/shield-check.svg +3 -0
  688. nautobot/project-static/nautobot-icons/sitemap-outline.svg +3 -0
  689. nautobot/project-static/nautobot-icons/sliders-vert-2.svg +3 -0
  690. nautobot/project-static/nautobot-icons/sliders-vert.svg +3 -0
  691. nautobot/project-static/nautobot-icons/star-filled.svg +3 -0
  692. nautobot/project-static/nautobot-icons/star.svg +3 -0
  693. nautobot/project-static/nautobot-icons/transform.svg +3 -0
  694. nautobot/project-static/nautobot-icons/wifi.svg +3 -0
  695. nautobot/tenancy/api/serializers.py +1 -0
  696. nautobot/tenancy/api/views.py +2 -1
  697. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  698. nautobot/tenancy/navigation.py +3 -1
  699. nautobot/tenancy/templates/tenancy/tenant_create.html +6 -6
  700. nautobot/tenancy/tests/test_filters.py +0 -2
  701. nautobot/tenancy/views.py +2 -1
  702. nautobot/ui/.gitignore +137 -0
  703. nautobot/ui/.node-version +1 -0
  704. nautobot/ui/.prettierignore +3 -0
  705. nautobot/ui/eslint.config.js +33 -0
  706. nautobot/ui/package-lock.json +6594 -0
  707. nautobot/ui/package.json +67 -0
  708. nautobot/ui/prettier.config.js +9 -0
  709. nautobot/ui/src/js/collapse.js +69 -0
  710. nautobot/ui/src/js/cookie.js +31 -0
  711. nautobot/ui/src/js/draggable.js +101 -0
  712. nautobot/ui/src/js/drawer.js +106 -0
  713. nautobot/ui/src/js/form.js +23 -0
  714. nautobot/ui/src/js/history.js +51 -0
  715. nautobot/ui/src/js/nautobot-graphiql.js +19 -0
  716. nautobot/ui/src/js/nautobot.js +128 -0
  717. nautobot/ui/src/js/search.js +274 -0
  718. nautobot/ui/src/js/select2.js +318 -0
  719. nautobot/ui/src/js/sidenav.js +87 -0
  720. nautobot/ui/src/js/tabs.js +139 -0
  721. nautobot/ui/src/js/theme.js +104 -0
  722. nautobot/ui/src/js/utils.js +54 -0
  723. nautobot/ui/src/scss/colors.scss +58 -0
  724. nautobot/ui/src/scss/nautobot.scss +2471 -0
  725. nautobot/ui/webpack.config.js +148 -0
  726. nautobot/users/apps.py +3 -0
  727. nautobot/users/filters.py +7 -11
  728. nautobot/users/forms.py +10 -0
  729. nautobot/users/models.py +8 -0
  730. nautobot/users/templates/users/advanced_settings_edit.html +31 -21
  731. nautobot/users/templates/users/api_tokens.html +61 -51
  732. nautobot/users/templates/users/base.html +23 -31
  733. nautobot/users/templates/users/change_password.html +29 -19
  734. nautobot/users/templates/users/preferences.html +55 -45
  735. nautobot/users/templates/users/profile.html +45 -14
  736. nautobot/users/tests/test_api.py +4 -0
  737. nautobot/users/urls.py +2 -0
  738. nautobot/users/views.py +70 -2
  739. nautobot/virtualization/api/views.py +1 -1
  740. nautobot/virtualization/filters.py +18 -32
  741. nautobot/virtualization/forms.py +22 -59
  742. nautobot/virtualization/models.py +1 -19
  743. nautobot/virtualization/navigation.py +3 -1
  744. nautobot/virtualization/tables.py +10 -6
  745. nautobot/virtualization/templates/virtualization/cluster.html +13 -13
  746. nautobot/virtualization/templates/virtualization/cluster_edit.html +6 -6
  747. nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +1 -1
  748. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  749. nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +24 -16
  750. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +1 -1
  751. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +4 -4
  752. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +27 -25
  753. nautobot/virtualization/templates/virtualization/vminterface.html +5 -5
  754. nautobot/virtualization/templates/virtualization/vminterface_edit.html +27 -11
  755. nautobot/virtualization/tests/test_api.py +3 -0
  756. nautobot/virtualization/tests/test_models.py +20 -5
  757. nautobot/virtualization/tests/test_views.py +3 -5
  758. nautobot/virtualization/urls.py +0 -11
  759. nautobot/virtualization/views.py +5 -122
  760. nautobot/vpn/__init__.py +0 -0
  761. nautobot/vpn/api/serializers.py +113 -0
  762. nautobot/vpn/api/urls.py +19 -0
  763. nautobot/vpn/api/views.py +70 -0
  764. nautobot/vpn/apps.py +8 -0
  765. nautobot/vpn/choices.py +171 -0
  766. nautobot/vpn/factory.py +209 -0
  767. nautobot/vpn/filters.py +233 -0
  768. nautobot/vpn/forms.py +486 -0
  769. nautobot/vpn/homepage.py +19 -0
  770. nautobot/vpn/migrations/0001_initial.py +541 -0
  771. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  772. nautobot/vpn/migrations/__init__.py +0 -0
  773. nautobot/vpn/models.py +527 -0
  774. nautobot/vpn/navigation.py +98 -0
  775. nautobot/vpn/tables.py +380 -0
  776. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  777. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  778. nautobot/vpn/tests/__init__.py +0 -0
  779. nautobot/vpn/tests/test_api.py +341 -0
  780. nautobot/vpn/tests/test_filters.py +139 -0
  781. nautobot/vpn/tests/test_forms.py +294 -0
  782. nautobot/vpn/tests/test_models.py +97 -0
  783. nautobot/vpn/tests/test_views.py +281 -0
  784. nautobot/vpn/urls.py +16 -0
  785. nautobot/vpn/views.py +437 -0
  786. nautobot/wireless/filters.py +0 -8
  787. nautobot/wireless/navigation.py +3 -1
  788. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +6 -6
  789. nautobot/wireless/tests/integration/test_radio_profile.py +3 -7
  790. nautobot/wireless/tests/test_api.py +1 -1
  791. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +5 -4
  792. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +802 -707
  793. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  794. nautobot/core/management/commands/check_job_approval_status.py +0 -47
  795. nautobot/core/templates/search_form.html +0 -9
  796. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +0 -87
  797. nautobot/dcim/filters/mixins.py +0 -354
  798. nautobot/extras/templates/extras/job_approval_request.html +0 -134
  799. nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +0 -28
  800. nautobot/ipam/mixins.py +0 -32
  801. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  802. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +0 -587
  803. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +0 -1
  804. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +0 -6
  805. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +0 -1
  806. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +0 -6865
  807. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +0 -1
  808. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +0 -6
  809. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +0 -1
  810. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
  811. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +0 -288
  812. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  813. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
  814. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  815. nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.js +0 -2580
  816. nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.min.js +0 -6
  817. nautobot/project-static/bootstrap-3.4.1-dist/js/npm.js +0 -13
  818. nautobot/project-static/clipboard.js-2.0.9/clipboard.min.js +0 -7
  819. nautobot/project-static/css/base.css +0 -1040
  820. nautobot/project-static/css/dark.css +0 -282
  821. nautobot/project-static/flatpickr-4.6.9/flatpickr.min.js +0 -2
  822. nautobot/project-static/flatpickr-4.6.9/themes/light.min.css +0 -1
  823. nautobot/project-static/graphiql-1.5.16/graphiql.min.css +0 -12
  824. nautobot/project-static/graphiql-1.5.16/graphiql.min.js +0 -11
  825. nautobot/project-static/highlight.js-11.9.0/github-dark.min.css +0 -10
  826. nautobot/project-static/highlight.js-11.9.0/github.min.css +0 -10
  827. nautobot/project-static/highlight.js-11.9.0/highlight.min.js +0 -378
  828. nautobot/project-static/jquery/jquery-3.7.1.min.js +0 -2
  829. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_444444_256x240.png +0 -0
  830. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_555555_256x240.png +0 -0
  831. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777620_256x240.png +0 -0
  832. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777777_256x240.png +0 -0
  833. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_cc0000_256x240.png +0 -0
  834. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_ffffff_256x240.png +0 -0
  835. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +0 -7
  836. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +0 -6
  837. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +0 -5
  838. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.theme.min.css +0 -5
  839. nautobot/project-static/js/homepage_layout.js +0 -182
  840. nautobot/project-static/js/nav_menu.js +0 -250
  841. nautobot/project-static/js/theme.js +0 -133
  842. nautobot/project-static/materialdesignicons-7.4.47/LICENSE +0 -20
  843. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +0 -3
  844. nautobot/project-static/react-16.14.0/react.production.min.js +0 -32
  845. nautobot/project-static/react-dom-16.14.0/react-dom.production.min.js +0 -239
  846. nautobot/project-static/select2-4.0.13/i18n/af.js +0 -3
  847. nautobot/project-static/select2-4.0.13/i18n/ar.js +0 -3
  848. nautobot/project-static/select2-4.0.13/i18n/az.js +0 -3
  849. nautobot/project-static/select2-4.0.13/i18n/bg.js +0 -3
  850. nautobot/project-static/select2-4.0.13/i18n/bn.js +0 -3
  851. nautobot/project-static/select2-4.0.13/i18n/bs.js +0 -3
  852. nautobot/project-static/select2-4.0.13/i18n/ca.js +0 -3
  853. nautobot/project-static/select2-4.0.13/i18n/cs.js +0 -3
  854. nautobot/project-static/select2-4.0.13/i18n/da.js +0 -3
  855. nautobot/project-static/select2-4.0.13/i18n/de.js +0 -3
  856. nautobot/project-static/select2-4.0.13/i18n/dsb.js +0 -3
  857. nautobot/project-static/select2-4.0.13/i18n/el.js +0 -3
  858. nautobot/project-static/select2-4.0.13/i18n/en.js +0 -3
  859. nautobot/project-static/select2-4.0.13/i18n/es.js +0 -3
  860. nautobot/project-static/select2-4.0.13/i18n/et.js +0 -3
  861. nautobot/project-static/select2-4.0.13/i18n/eu.js +0 -3
  862. nautobot/project-static/select2-4.0.13/i18n/fa.js +0 -3
  863. nautobot/project-static/select2-4.0.13/i18n/fi.js +0 -3
  864. nautobot/project-static/select2-4.0.13/i18n/fr.js +0 -3
  865. nautobot/project-static/select2-4.0.13/i18n/gl.js +0 -3
  866. nautobot/project-static/select2-4.0.13/i18n/he.js +0 -3
  867. nautobot/project-static/select2-4.0.13/i18n/hi.js +0 -3
  868. nautobot/project-static/select2-4.0.13/i18n/hr.js +0 -3
  869. nautobot/project-static/select2-4.0.13/i18n/hsb.js +0 -3
  870. nautobot/project-static/select2-4.0.13/i18n/hu.js +0 -3
  871. nautobot/project-static/select2-4.0.13/i18n/hy.js +0 -3
  872. nautobot/project-static/select2-4.0.13/i18n/id.js +0 -3
  873. nautobot/project-static/select2-4.0.13/i18n/is.js +0 -3
  874. nautobot/project-static/select2-4.0.13/i18n/it.js +0 -3
  875. nautobot/project-static/select2-4.0.13/i18n/ja.js +0 -3
  876. nautobot/project-static/select2-4.0.13/i18n/ka.js +0 -3
  877. nautobot/project-static/select2-4.0.13/i18n/km.js +0 -3
  878. nautobot/project-static/select2-4.0.13/i18n/ko.js +0 -3
  879. nautobot/project-static/select2-4.0.13/i18n/lt.js +0 -3
  880. nautobot/project-static/select2-4.0.13/i18n/lv.js +0 -3
  881. nautobot/project-static/select2-4.0.13/i18n/mk.js +0 -3
  882. nautobot/project-static/select2-4.0.13/i18n/ms.js +0 -3
  883. nautobot/project-static/select2-4.0.13/i18n/nb.js +0 -3
  884. nautobot/project-static/select2-4.0.13/i18n/ne.js +0 -3
  885. nautobot/project-static/select2-4.0.13/i18n/nl.js +0 -3
  886. nautobot/project-static/select2-4.0.13/i18n/pl.js +0 -3
  887. nautobot/project-static/select2-4.0.13/i18n/ps.js +0 -3
  888. nautobot/project-static/select2-4.0.13/i18n/pt-BR.js +0 -3
  889. nautobot/project-static/select2-4.0.13/i18n/pt.js +0 -3
  890. nautobot/project-static/select2-4.0.13/i18n/ro.js +0 -3
  891. nautobot/project-static/select2-4.0.13/i18n/ru.js +0 -3
  892. nautobot/project-static/select2-4.0.13/i18n/sk.js +0 -3
  893. nautobot/project-static/select2-4.0.13/i18n/sl.js +0 -3
  894. nautobot/project-static/select2-4.0.13/i18n/sq.js +0 -3
  895. nautobot/project-static/select2-4.0.13/i18n/sr-Cyrl.js +0 -3
  896. nautobot/project-static/select2-4.0.13/i18n/sr.js +0 -3
  897. nautobot/project-static/select2-4.0.13/i18n/sv.js +0 -3
  898. nautobot/project-static/select2-4.0.13/i18n/th.js +0 -3
  899. nautobot/project-static/select2-4.0.13/i18n/tk.js +0 -3
  900. nautobot/project-static/select2-4.0.13/i18n/tr.js +0 -3
  901. nautobot/project-static/select2-4.0.13/i18n/uk.js +0 -3
  902. nautobot/project-static/select2-4.0.13/i18n/vi.js +0 -3
  903. nautobot/project-static/select2-4.0.13/i18n/zh-CN.js +0 -3
  904. nautobot/project-static/select2-4.0.13/i18n/zh-TW.js +0 -3
  905. nautobot/project-static/select2-4.0.13/select2.min.css +0 -1
  906. nautobot/project-static/select2-4.0.13/select2.min.js +0 -2
  907. nautobot/project-static/select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css +0 -7
  908. nautobot/project-static/subscriptions-transport-ws-0.9.18/client.min.js +0 -8
  909. nautobot/project-static/whatwg-fetch-3.6.2/fetch.umd.min.js +0 -8
  910. nautobot/virtualization/templates/virtualization/cluster_add_devices.html +0 -37
  911. /nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +0 -0
  912. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.ttf → dist/1fcc36272ea3e53d0031.ttf} +0 -0
  913. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.eot → dist/2146c3c82b553977abc7.eot} +0 -0
  914. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff → dist/e55a20c80650829ec5fd.woff} +0 -0
  915. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 → dist/ec024da790d2972da002.woff2} +0 -0
  916. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  917. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  918. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  919. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
@@ -4,6 +4,8 @@ import contextlib
4
4
  from dataclasses import dataclass
5
5
  from enum import Enum
6
6
  import logging
7
+ from typing import Any
8
+ import uuid
7
9
 
8
10
  from django.contrib.contenttypes.models import ContentType
9
11
  from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
@@ -35,14 +37,17 @@ from nautobot.core.templatetags.helpers import (
35
37
  render_json,
36
38
  render_markdown,
37
39
  slugify,
40
+ table_config_form,
38
41
  validated_viewname,
39
42
  )
40
43
  from nautobot.core.ui.choices import LayoutChoices, SectionChoices
44
+ from nautobot.core.ui.echarts import EChartsBase
41
45
  from nautobot.core.ui.utils import render_component_template
42
46
  from nautobot.core.utils.lookup import get_filterset_for_model, get_route_for_model
43
47
  from nautobot.core.utils.permissions import get_permission_for_model
44
48
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
45
49
  from nautobot.core.views.utils import get_obj_from_context
50
+ from nautobot.data_validation.tables import DataComplianceTable
46
51
  from nautobot.dcim.models import Rack
47
52
  from nautobot.extras.choices import CustomFieldTypeChoices
48
53
  from nautobot.extras.tables import AssociatedContactsTable, DynamicGroupTable, ObjectMetadataTable
@@ -97,6 +102,7 @@ class ObjectDetailContent:
97
102
  _ObjectDetailContactsTab(),
98
103
  _ObjectDetailGroupsTab(),
99
104
  _ObjectDetailMetadataTab(),
105
+ _ObjectDetailDataComplianceTab(),
100
106
  ]
101
107
  if extra_tabs is not None:
102
108
  tabs.extend(extra_tabs)
@@ -197,7 +203,7 @@ class Button(Component):
197
203
  size=None,
198
204
  link_includes_pk=True,
199
205
  context_object_key=None,
200
- render_on_tab_id="__all__",
206
+ render_on_tab_id="main",
201
207
  **kwargs,
202
208
  ):
203
209
  """
@@ -376,8 +382,9 @@ class Tab(Component):
376
382
  WEIGHT_CONTACTS_TAB = 300
377
383
  WEIGHT_GROUPS_TAB = 400
378
384
  WEIGHT_METADATA_TAB = 500
379
- WEIGHT_NOTES_TAB = 600 # reserved, not yet using this framework
380
- WEIGHT_CHANGELOG_TAB = 700 # reserved, not yet using this framework
385
+ WEIGHT_DATACOMPLIANCE_TAB = 600
386
+ WEIGHT_NOTES_TAB = 700 # reserved, not yet using this framework
387
+ WEIGHT_CHANGELOG_TAB = 800 # reserved, not yet using this framework
381
388
 
382
389
  def panels_for_section(self, section):
383
390
  """
@@ -420,6 +427,9 @@ class Tab(Component):
420
427
  """
421
428
  Only render a main-view Tab if the active request is for the main object view rather than a separate action.
422
429
  """
430
+ if not self.should_render(context):
431
+ return False
432
+
423
433
  request = context["request"]
424
434
  obj = get_obj_from_context(context)
425
435
  return request.path == obj.get_absolute_url()
@@ -435,6 +445,7 @@ class Tab(Component):
435
445
  "tab_id": self.tab_id,
436
446
  "label": self.render_label(context),
437
447
  "include_plugin_content": self.tab_id == "main",
448
+ "include_timestamps_and_buttons": self.tab_id == "main",
438
449
  "left_half_panels": self.panels_for_section(SectionChoices.LEFT_HALF),
439
450
  "right_half_panels": self.panels_for_section(SectionChoices.RIGHT_HALF),
440
451
  "full_width_panels": self.panels_for_section(SectionChoices.FULL_WIDTH),
@@ -504,6 +515,9 @@ class DistinctViewTab(Tab):
504
515
  """
505
516
  A DistinctViewTab should only render its content if the view in question is active.
506
517
  """
518
+ if not self.should_render(context):
519
+ return False
520
+
507
521
  with context.update(self.get_extra_context(context)):
508
522
  request = context["request"]
509
523
  return request.path == context["url"]
@@ -581,6 +595,8 @@ class Panel(Component):
581
595
  """
582
596
  if not self.should_render(context):
583
597
  return ""
598
+ if not self.body_id:
599
+ self.body_id = self._get_body_id(context)
584
600
  with context.update(self.get_extra_context(context)):
585
601
  return render_component_template(
586
602
  self.template_path,
@@ -590,11 +606,21 @@ class Panel(Component):
590
606
  header_extra_content=self.render_header_extra_content(context),
591
607
  body=self.render_body(context),
592
608
  footer_content=self.render_footer_content(context),
609
+ body_id=self.body_id,
593
610
  )
594
611
 
612
+ def _get_body_id(self, context: Context):
613
+ """Retreive the `body_id` attribute to the rendered components, used for the collapsible panel feature."""
614
+ if self.body_id:
615
+ return self.body_id
616
+ if self.label:
617
+ return slugify(self.label)
618
+
619
+ return str(uuid.uuid4())
620
+
595
621
  def render_label(self, context: Context):
596
622
  """Render the label of this panel, if any."""
597
- return self.label
623
+ return self.label.upper()
598
624
 
599
625
  def render_header_extra_content(self, context: Context):
600
626
  """
@@ -790,8 +816,8 @@ class ObjectsTablePanel(Panel):
790
816
  If None, defaults to the `get_paginate_count()` (which is user's preference or a global setting).
791
817
  paginate (bool, optional): If False, do not attach a paginator to the table and render all rows
792
818
  (or up to `max_display_count` if provided). Defaults to True.
793
- show_table_config_button (bool, optional): If False, hide the small "Configure" button rendered in the
794
- panel header for this table. Defaults to True.
819
+ show_table_config_button (bool, optional): If False, do not allow user configuration of the table.
820
+ Defaults to True.
795
821
  table_title (str, optional): The title to display in the panel heading for the table.
796
822
  If None, defaults to the plural verbose name of the table model.
797
823
  include_columns (list, optional): A list of field names to include in the table display.
@@ -904,6 +930,16 @@ class ObjectsTablePanel(Panel):
904
930
 
905
931
  return body_content_table_add_url
906
932
 
933
+ def render_table_config_form(self, context: Context):
934
+ if not self.should_render(context):
935
+ return ""
936
+ if not self.show_table_config_button:
937
+ return ""
938
+ context = self.get_extra_context(context)
939
+ return render_to_string(
940
+ "utilities/templatetags/table_config_form.html", table_config_form(context["body_content_table"])
941
+ )
942
+
907
943
  def get_extra_context(self, context: Context):
908
944
  """Add additional context for rendering the table panel.
909
945
 
@@ -946,7 +982,10 @@ class ObjectsTablePanel(Panel):
946
982
  if self.distinct:
947
983
  body_content_table_queryset = body_content_table_queryset.distinct()
948
984
  body_content_table = body_content_table_class(
949
- body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui, user=request.user
985
+ body_content_table_queryset,
986
+ hide_hierarchy_ui=self.hide_hierarchy_ui,
987
+ user=request.user,
988
+ configurable=self.show_table_config_button,
950
989
  )
951
990
  if self.tab_id and "actions" in body_content_table.columns:
952
991
  # Use the `self.tab_id`, if it exists, to determine the correct return URL for the table
@@ -1022,7 +1061,7 @@ class ObjectsTablePanel(Panel):
1022
1061
  "form_id": self.form_id,
1023
1062
  "more_queryset_count": more_queryset_count,
1024
1063
  "include_paginator": self.include_paginator,
1025
- "show_table_config_button": self.show_table_config_button,
1064
+ "show_table_config_button": self.show_table_config_button, # unused now in core but kept for compatibility
1026
1065
  }
1027
1066
 
1028
1067
 
@@ -1226,10 +1265,11 @@ class KeyValueTablePanel(Panel):
1226
1265
  else:
1227
1266
  value_tag = format_html(
1228
1267
  """
1229
- <span class="hover_copy">
1268
+ <span>
1230
1269
  <span id="{unique_id}_value_{key}">{value}</span>
1231
- <button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{unique_id}_value_{key}">
1232
- <span class="mdi mdi-content-copy"></span>
1270
+ <button class="btn btn-secondary nb-btn-inline-hover" data-clipboard-target="#{unique_id}_value_{key}">
1271
+ <span aria-hidden="true" class="mdi mdi-content-copy"></span>
1272
+ <span class="visually-hidden">Copy</span>
1233
1273
  </button>
1234
1274
  </span>
1235
1275
  """,
@@ -1244,6 +1284,83 @@ class KeyValueTablePanel(Panel):
1244
1284
  return result
1245
1285
 
1246
1286
 
1287
+ class EChartsPanel(Panel, EChartsBase):
1288
+ """A panel that renders ECharts charts using the EChartsBase class."""
1289
+
1290
+ def __init__(
1291
+ self,
1292
+ *,
1293
+ chart_kwargs=None,
1294
+ width="100%",
1295
+ height="32rem",
1296
+ chart_container_id=None,
1297
+ body_wrapper_template_path="components/echarts.html",
1298
+ **kwargs,
1299
+ ):
1300
+ """
1301
+ Initialize an ECharts panel.
1302
+
1303
+ Args:
1304
+ chart_kwargs (dict): Kwargs to pass to EChartsBase constructor.
1305
+ width (str): CSS width for the chart container (default: "100%").
1306
+ height (str): CSS height for the chart container (default: "32rem").
1307
+ chart_container_id (str): Custom HTML ID for the chart container. If None, auto-generated.
1308
+ """
1309
+ self.width = width
1310
+ self.height = height
1311
+ self.chart_container_id = chart_container_id
1312
+ self.body_id = (
1313
+ self.chart_container_id or f"{slugify('echart-' + chart_kwargs.get('header', ''))}-{uuid.uuid4().hex[:8]}"
1314
+ )
1315
+
1316
+ super().__init__(body_wrapper_template_path=body_wrapper_template_path, body_id=self.body_id, **kwargs)
1317
+ EChartsBase.__init__(self, **chart_kwargs)
1318
+
1319
+ def get_data(self, context: Context) -> dict[str, Any] | None:
1320
+ """Get the data for chart.
1321
+
1322
+ Args:
1323
+ context (Context): The template or request context.
1324
+
1325
+ Returns:
1326
+ dict[str, Any] | None:
1327
+ - A dictionary in internal chart format, e.g.:
1328
+ {"x": [...], "series": [{"name": str, "data": [...]}]}
1329
+ - A nested dictionary of series, e.g.:
1330
+ {"Series1": {"x1": val1, "x2": val2}, ...}
1331
+ - `None` if no data is set.
1332
+ """
1333
+ if callable(self.data):
1334
+ return self.data(context) # pylint: disable=not-callable
1335
+ return self.data
1336
+
1337
+ def should_render(self, context: Context):
1338
+ """Determine if the panel should be rendered."""
1339
+ if not super().should_render(context):
1340
+ return False
1341
+
1342
+ # Check permissions if specified
1343
+ if self.permission:
1344
+ request = context.get("request")
1345
+ if request and hasattr(request, "user"):
1346
+ return request.user.has_perm(self.permission)
1347
+
1348
+ return True
1349
+
1350
+ def get_extra_context(self, context: Context):
1351
+ """Add chart-specific context variables."""
1352
+ self.data = self.get_data(context)
1353
+ chart_config = self.get_config()
1354
+ return {
1355
+ **super().get_extra_context(context),
1356
+ "chart": self,
1357
+ "chart_config": chart_config,
1358
+ "chart_width": self.width,
1359
+ "chart_height": self.height,
1360
+ "chart_container_id": self.body_id,
1361
+ }
1362
+
1363
+
1247
1364
  class ObjectFieldsPanel(KeyValueTablePanel):
1248
1365
  """A panel that renders a table of object instance attributes and their values."""
1249
1366
 
@@ -1291,7 +1408,7 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1291
1408
  def render_label(self, context: Context):
1292
1409
  """Default to rendering the provided object's `verbose_name` if no more specific `label` was defined."""
1293
1410
  if self.label is None:
1294
- return bettertitle(get_obj_from_context(context, self.context_object_key)._meta.verbose_name)
1411
+ return get_obj_from_context(context, self.context_object_key)._meta.verbose_name.upper()
1295
1412
  return super().render_label(context)
1296
1413
 
1297
1414
  def render_value(self, key, value, context: Context):
@@ -1437,10 +1554,19 @@ class GroupedKeyValueTablePanel(KeyValueTablePanel):
1437
1554
  super().__init__(body_id=body_id, **kwargs)
1438
1555
 
1439
1556
  def render_header_extra_content(self, context: Context):
1440
- """Add a "Collapse All" button to the header."""
1557
+ """Add a "Collapse All Groups" button to the header."""
1441
1558
  return format_html(
1442
- '<button type="button" class="btn-xs btn-primary pull-right accordion-toggle-all" data-target="#{body_id}">'
1443
- "Collapse All</button>",
1559
+ """
1560
+ <button
1561
+ aria-expanded="true"
1562
+ class="btn btn-primary btn-sm float-end"
1563
+ data-nb-target="[class^=&quot;collapseme-{body_id}-&quot;]"
1564
+ data-nb-toggle="collapse-all"
1565
+ type="button"
1566
+ >
1567
+ Collapse All Groups
1568
+ </button>
1569
+ """,
1444
1570
  body_id=self.body_id,
1445
1571
  )
1446
1572
 
@@ -1469,7 +1595,7 @@ class GroupedKeyValueTablePanel(KeyValueTablePanel):
1469
1595
  if value_display:
1470
1596
  # TODO: add a copy button on hover to all display items
1471
1597
  result += format_html(
1472
- '<tr class="collapseme-{body_id}-{counter} collapse in" data-parent="#{body_id}">'
1598
+ '<tr class="collapseme-{body_id}-{counter} collapse show nb-transition-none">'
1473
1599
  "<td>{key}</td><td>{value}</td></tr>",
1474
1600
  counter=counter,
1475
1601
  body_id=self.body_id,
@@ -2019,6 +2145,7 @@ class _ObjectDetailContactsTab(Tab):
2019
2145
  # TODO: we should provide a standard reusable component template for bulk-actions in the footer
2020
2146
  footer_content_template_path="components/panel/footer_contacts_table.html",
2021
2147
  header_extra_content_template_path=None,
2148
+ label="Contacts/Teams",
2022
2149
  ),
2023
2150
  )
2024
2151
  super().__init__(tab_id=tab_id, label=label, weight=weight, panels=panels, **kwargs)
@@ -2039,6 +2166,42 @@ class _ObjectDetailContactsTab(Tab):
2039
2166
  )
2040
2167
 
2041
2168
 
2169
+ class _ObjectDetailDataComplianceTab(DistinctViewTab):
2170
+ """Built-in class for a Tab displaying information about data compliance."""
2171
+
2172
+ def __init__(
2173
+ self,
2174
+ *,
2175
+ tab_id="data_compliance",
2176
+ label="Data Compliance",
2177
+ weight=Tab.WEIGHT_DATACOMPLIANCE_TAB,
2178
+ panels=None,
2179
+ **kwargs,
2180
+ ):
2181
+ if panels is None:
2182
+ panels = (
2183
+ ObjectsTablePanel(
2184
+ weight=100,
2185
+ table_class=DataComplianceTable,
2186
+ table_attribute="associated_data_compliance",
2187
+ related_field_name="object_id",
2188
+ label="Data Compliance",
2189
+ add_button_route=None,
2190
+ header_extra_content_template_path=None,
2191
+ include_paginator=True,
2192
+ ),
2193
+ )
2194
+ super().__init__(url_name="", tab_id=tab_id, label=label, weight=weight, panels=panels, **kwargs)
2195
+
2196
+ def get_extra_context(self, context: Context):
2197
+ return {"url": get_obj_from_context(context).get_data_compliance_url()}
2198
+
2199
+ def should_render(self, context: Context):
2200
+ if not super().should_render(context):
2201
+ return False
2202
+ return getattr(get_obj_from_context(context), "is_data_compliance_model", False)
2203
+
2204
+
2042
2205
  class DynamicGroupsTextPanel(BaseTextPanel):
2043
2206
  """Panel displaying a note about caching of dynamic groups."""
2044
2207
 
@@ -2145,6 +2308,7 @@ class _ObjectDetailMetadataTab(Tab):
2145
2308
  add_button_route=None,
2146
2309
  related_field_name="assigned_object_id",
2147
2310
  header_extra_content_template_path=None,
2311
+ label="Object Metadata",
2148
2312
  ),
2149
2313
  )
2150
2314
  super().__init__(
nautobot/core/urls.py CHANGED
@@ -1,10 +1,11 @@
1
1
  from django.conf import settings
2
2
  from django.http import HttpResponse, HttpResponseNotFound
3
3
  from django.urls import include, path
4
- from django.views.generic import TemplateView
4
+ from django.views.generic import RedirectView, TemplateView
5
5
 
6
6
  from nautobot.core.views import (
7
7
  AboutView,
8
+ AppDocsView,
8
9
  CustomGraphQLView,
9
10
  get_file_with_authorization,
10
11
  HomeView,
@@ -37,6 +38,7 @@ urlpatterns = [
37
38
  # Apps
38
39
  path("circuits/", include("nautobot.circuits.urls")),
39
40
  path("cloud/", include("nautobot.cloud.urls")),
41
+ path("data-validation/", include("nautobot.data_validation.urls")),
40
42
  path("dcim/", include("nautobot.dcim.urls")),
41
43
  path("extras/", include("nautobot.extras.urls")),
42
44
  path("ipam/", include("nautobot.ipam.urls")),
@@ -45,6 +47,7 @@ urlpatterns = [
45
47
  path("user/", include("nautobot.users.urls")),
46
48
  path("users/", include("nautobot.users.urls", "users")),
47
49
  path("virtualization/", include("nautobot.virtualization.urls")),
50
+ path("vpn/", include("nautobot.vpn.urls")),
48
51
  path("wireless/", include("nautobot.wireless.urls")),
49
52
  # API
50
53
  path("api/", include("nautobot.core.api.urls")),
@@ -58,6 +61,15 @@ urlpatterns = [
58
61
  path("media-failure/", StaticMediaFailureView.as_view(), name="media_failure"),
59
62
  # Apps
60
63
  path("apps/", include((apps_patterns, "apps"))),
64
+ # Redirect /docs/<app_base_url>/ -> /docs/<app_base_url>/index.html
65
+ path(
66
+ "docs/<str:app_base_url>/",
67
+ RedirectView.as_view(pattern_name="docs_file", permanent=False),
68
+ kwargs={"path": "index.html"},
69
+ name="docs_index_redirect",
70
+ ),
71
+ # Apps docs - Serve docs file
72
+ path("docs/<str:app_base_url>/<path:path>", AppDocsView.as_view(), name="docs_file"),
61
73
  path("plugins/", include((plugin_patterns, "plugins"))),
62
74
  path("admin/plugins/", include(plugin_admin_patterns)),
63
75
  # Social auth/SSO
@@ -0,0 +1,71 @@
1
+ """Utilities for conveniently working with the Django/Redis cache."""
2
+
3
+ import logging
4
+
5
+ from django.conf import settings
6
+ from django.db import models
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def construct_cache_key(obj, *, method_name=None, branch_aware=True, **params):
12
+ """
13
+ Construct a consistently-structured Django/Redis cache key for the given obj and/or method name.
14
+
15
+ Args:
16
+ obj (Any): A model class, model instance, model manager, class, or function that will make use of the cache.
17
+ method_name (str): Name of a specific method on `obj`. May be omitted only if `obj` is itself a function.
18
+ branch_aware (bool): Whether this cache key needs to vary by branch when Version Control is enabled.
19
+ **params (dict): Parameters that should further narrow the scope of the cache key.
20
+
21
+ Examples:
22
+ >>> construct_cache_key(Location.objects, method_name="max_depth")
23
+ 'nautobot.dcim.location.max_depth'
24
+ >>> construct_cache_key(MinMaxValidationRule, method_name="get_for_model")
25
+ 'nautobot.data_validation.minmaxvalidationrule.get_for_model'
26
+ >>> construct_cache_key(MinMaxValidationRule, method_name="get_for_model", content_type="dcim.location")
27
+ 'nautobot.data_validation.minmaxvalidationrule.get_for_model(content_type=dcim.location)'
28
+ >>> construct_cache_key(CustomField.objects, method_name="get_for_model", model="dcim.location", exclude_filter_disabled=True, listing=True)
29
+ 'nautobot.extras.customfield.get_for_model(model=dcim.location,exclude_filter_disabled=True,listing=True)'
30
+ >>> from nautobot.extras.utils import change_logged_models_queryset
31
+ >>> construct_cache_key(change_logged_models_queryset)
32
+ 'nautobot.extras.utils.change_logged_models_queryset'
33
+ """
34
+ method_name_must_be_set = True
35
+ if isinstance(obj, models.Model):
36
+ tokens = ["nautobot", obj._meta.concrete_model._meta.label_lower, str(obj.pk), method_name]
37
+ elif isinstance(obj, models.Manager):
38
+ tokens = ["nautobot", obj.model._meta.concrete_model._meta.label_lower, method_name]
39
+ elif isinstance(obj, type):
40
+ # A class object
41
+ if issubclass(obj, models.Model):
42
+ tokens = ["nautobot", obj._meta.concrete_model._meta.label_lower, method_name]
43
+ elif issubclass(obj, models.Manager):
44
+ tokens = ["nautobot", obj.model._meta.concrete_model._meta.label_lower, method_name]
45
+ else:
46
+ tokens = [obj.__module__, obj.__name__, method_name]
47
+ elif method_name is not None:
48
+ # An instance of any class not specifically handled above
49
+ tokens = [obj.__module__, obj.__class__.__name__, method_name]
50
+ else:
51
+ # A standalone function
52
+ tokens = [obj.__module__, obj.__name__]
53
+ method_name_must_be_set = False
54
+
55
+ if method_name_must_be_set and method_name is None:
56
+ raise ValueError("method_name must be specified for the given obj")
57
+
58
+ if branch_aware and "nautobot_version_control" in settings.PLUGINS:
59
+ from nautobot_version_control.utils import active_branch # pylint: disable=import-error
60
+
61
+ tokens += ["branch", active_branch()]
62
+
63
+ cache_key = ".".join(tokens)
64
+
65
+ params_tokens = [f"{key}={value}" for key, value in params.items()]
66
+ if params_tokens:
67
+ cache_key += f"({','.join(params_tokens)})"
68
+
69
+ # Disabled as it's very noisy in some cases
70
+ # logger.debug("Constructed cache key is %s", cache_key)
71
+ return cache_key
@@ -28,9 +28,10 @@ def flatten_dict(d, prefix="", separator="."):
28
28
  """
29
29
  Flatten nested dictionaries into a single level by joining key names with a separator.
30
30
 
31
- :param d: The dictionary to be flattened
32
- :param prefix: Initial prefix (if any)
33
- :param separator: The character to use when concatenating key names
31
+ Args:
32
+ d (dict): The dictionary to be flattened
33
+ prefix (str): Initial prefix (if any)
34
+ separator (str): The character to use when concatenating key names
34
35
  """
35
36
  ret = {}
36
37
  for k, v in d.items():
@@ -46,8 +47,10 @@ def flatten_iterable(iterable):
46
47
  """
47
48
  Flatten a nested iterable such as a list of lists, keeping strings intact.
48
49
 
49
- :param iterable: The iterable to be flattened
50
- :returns: generator
50
+ Args:
51
+ iterable (Iterable): The iterable to be flattened
52
+
53
+ Yields: str
51
54
  """
52
55
  for i in iterable:
53
56
  if hasattr(i, "__iter__") and not isinstance(i, str):
@@ -18,12 +18,18 @@ from nautobot.core.utils.lookup import get_filterset_for_model
18
18
  CONTAINS_LOOKUP_EXPR_RE = re.compile(r"(?<=__)\w+")
19
19
 
20
20
  MODEL_VERBOSE_NAME_PLURAL_TO_FEATURE_NAME_MAPPING = {
21
+ "approval_workflow_definitions": "approval_workflows",
21
22
  "cables": "cable_terminations",
23
+ "data_compliance": "custom_validators",
24
+ "location_types": "locations",
22
25
  "metadata_types": "metadata",
26
+ "min_max_validation_rules": "custom_validators",
23
27
  "object_metadata": "metadata",
24
- "location_types": "locations",
25
- "static_group_associations": "dynamic_groups",
28
+ "regular_expression_validation_rules": "custom_validators",
26
29
  "relationship_associations": "relationships",
30
+ "required_validation_rules": "custom_validators",
31
+ "static_group_associations": "dynamic_groups",
32
+ "unique_validation_rules": "custom_validators",
27
33
  }
28
34
 
29
35
 
@@ -41,10 +41,10 @@ def swap_status_initials(data):
41
41
 
42
42
  def convert_git_diff_log_to_list(logs):
43
43
  """
44
- Convert Git diff log into a list splitted by \\n
44
+ Convert Git diff log into a list splitted by `\\n`
45
45
 
46
- Example:
47
- >>> git_log = "M\tindex.html\nR\tsample.txt"
46
+ Examples:
47
+ >>> git_log = "M\\tindex.html\\nR\\tsample.txt"
48
48
  >>> print(convert_git_diff_log_to_list(git_log))
49
49
  ["Modification - index.html", "Renaming - sample.txt"]
50
50
  """