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
@@ -1093,7 +1093,15 @@ class JobButtonViewSet(NotesViewSetMixin, ModelViewSet):
1093
1093
  #
1094
1094
 
1095
1095
 
1096
- class ScheduledJobViewSet(ReadOnlyModelViewSet):
1096
+ class ScheduledJobViewSet(
1097
+ # DRF mixins:
1098
+ # note no CreateModelMixin or UpdateModelMixin
1099
+ mixins.DestroyModelMixin,
1100
+ # Nautobot mixins:
1101
+ BulkDestroyModelMixin,
1102
+ # Base class
1103
+ ReadOnlyModelViewSet,
1104
+ ):
1097
1105
  """
1098
1106
  Retrieve a list of scheduled jobs
1099
1107
  """
@@ -1,7 +1,6 @@
1
1
  from celery import states
2
2
 
3
3
  from nautobot.core.choices import ChoiceSet
4
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
5
4
 
6
5
  #
7
6
  # Approval Workflows
@@ -159,7 +158,7 @@ class CustomFieldTypeChoices(ChoiceSet):
159
158
 
160
159
 
161
160
  class ButtonClassChoices(ChoiceSet):
162
- CLASS_DEFAULT = "default"
161
+ CLASS_DEFAULT = "default" # maps to "secondary" in v3 UI, but kept for backwards compatibility
163
162
  CLASS_PRIMARY = "primary"
164
163
  CLASS_SUCCESS = "success"
165
164
  CLASS_INFO = "info"
@@ -171,23 +170,13 @@ class ButtonClassChoices(ChoiceSet):
171
170
  (CLASS_DEFAULT, "Default"),
172
171
  (CLASS_PRIMARY, "Primary (blue)"),
173
172
  (CLASS_SUCCESS, "Success (green)"),
174
- (CLASS_INFO, "Info (aqua)"),
173
+ (CLASS_INFO, "Info (blue)"),
175
174
  (CLASS_WARNING, "Warning (orange)"),
176
175
  (CLASS_DANGER, "Danger (red)"),
177
176
  (CLASS_LINK, "None (link)"),
178
177
  )
179
178
 
180
179
 
181
- #
182
- # CustomLinks
183
- #
184
-
185
-
186
- @class_deprecated_in_favor_of(ButtonClassChoices)
187
- class CustomLinkButtonClassChoices(ButtonClassChoices):
188
- pass
189
-
190
-
191
180
  #
192
181
  # Dynamic Groups
193
182
  #
@@ -21,7 +21,7 @@ from nautobot.extras.choices import (
21
21
  CustomFieldTypeChoices,
22
22
  RelationshipSideChoices,
23
23
  )
24
- from nautobot.extras.filters.customfields import (
24
+ from nautobot.extras.filter_mixins_customfields import (
25
25
  CustomFieldBooleanFilter,
26
26
  CustomFieldCharFilter,
27
27
  CustomFieldDateFilter,
@@ -1,3 +1,6 @@
1
+ from functools import reduce
2
+ import operator
3
+
1
4
  from django.db.models import Q
2
5
  from django.forms import IntegerField
3
6
  import django_filters
@@ -36,19 +39,52 @@ class CustomFieldFilterMixin:
36
39
  super().__init__(*args, **kwargs)
37
40
  self.field_name = f"_custom_field_data__{self.field_name}"
38
41
 
42
+ def generate_query(self, value):
43
+ # This method may be called from extras.models.DynamicGroup._generate_query_for_filter method
44
+ # to generate proper query for given field. But at this point, we don't know if the field will be negated or not
45
+ # That's why we're preparing query that works both: for positional filtering and negated one.
46
+ # For positional filtering, when we're expecting some value, the field must exists (key in custom field data JSONB)
47
+ # and value can't be None (null in db)
48
+ # For negated filtering we're expecting field without some value, but key may be missing or value can be None (null in db)
49
+ # Please refer to the filter method below, for more context.
50
+ if value == "null" or value == ["null"]:
51
+ return Q(**{f"{self.field_name}__exact": None}) & Q(**{f"{self.field_name}__isnull": False})
52
+
53
+ if isinstance(value, (list, tuple)):
54
+ value_match = reduce(operator.or_, [Q(**{f"{self.field_name}__{self.lookup_expr}": v}) for v in value])
55
+ else:
56
+ value_match = Q(**{f"{self.field_name}__{self.lookup_expr}": value})
57
+ value_is_not_none = ~Q(**{f"{self.field_name}__exact": None})
58
+ key_is_present_in_jsonb = Q(
59
+ **{f"{self.field_name}__isnull": False}
60
+ ) # __isnull and __has_key has same output in case of JSONB fields
61
+
62
+ return value_match & value_is_not_none & key_is_present_in_jsonb
63
+
39
64
  def filter(self, qs, value):
40
65
  if value in django_filters.constants.EMPTY_VALUES:
41
66
  return qs
42
67
 
43
- if value == "null":
68
+ if value == "null" or value == ["null"]:
44
69
  return self.get_method(qs)(
45
- Q(**{f"{self.field_name}__exact": None}) | Q(**{f"{self.field_name}__isnull": True})
70
+ Q(**{f"{self.field_name}__exact": None}) & Q(**{f"{self.field_name}__isnull": False})
46
71
  )
47
72
 
48
73
  # Custom fields require special handling for exclude filtering.
49
- # Return custom fields that don't match the value and null custom fields
74
+ # Return custom fields that don't match the value, key is missing or value is set to null
75
+ # For JSONB fields, like `_custom_field_data`:
76
+ # __isnull and __has_key returns those records which has key
77
+ # __isnull is not checking the actual value in JSONB!
78
+ # to check for null value, we need to use exact
79
+ # With exclude filtering we need to take into account all cases:
80
+ # - no key - handled by __isnull check
81
+ # - key is present with null - handled by __exact=None
82
+ # - key is present with some value - handled by filter
83
+ # - key is present with empty str - handled by filter
50
84
  if self.exclude:
51
- qs_null_custom_fields = qs.filter(**{f"{self.field_name}__isnull": True}).distinct()
85
+ qs_null_custom_fields = qs.filter(
86
+ Q(**{f"{self.field_name}__isnull": True}) | Q(**{f"{self.field_name}__exact": None})
87
+ ).distinct()
52
88
  return super().filter(qs, value).distinct() | qs_null_custom_fields
53
89
 
54
90
  return super().filter(qs, value)
@@ -58,7 +94,7 @@ class CustomFieldBooleanFilter(CustomFieldFilterMixin, django_filters.BooleanFil
58
94
  """Custom field single value filter for backwards compatibility"""
59
95
 
60
96
 
61
- class CustomFieldCharFilter(CustomFieldFilterMixin, django_filters.Filter):
97
+ class CustomFieldCharFilter(CustomFieldFilterMixin, django_filters.CharFilter):
62
98
  """Custom field single value filter for backwards compatibility"""
63
99
 
64
100
 
@@ -92,7 +128,7 @@ class CustomFieldMultiSelectFilter(CustomFieldSelectFilter):
92
128
  super().__init__(*args, **kwargs)
93
129
 
94
130
 
95
- class CustomFieldNumberFilter(CustomFieldFilterMixin, django_filters.Filter):
131
+ class CustomFieldNumberFilter(CustomFieldFilterMixin, django_filters.NumberFilter):
96
132
  """Custom field single value filter for backwards compatibility"""
97
133
 
98
134
  field_class = IntegerField
@@ -23,7 +23,6 @@ from nautobot.core.filters import (
23
23
  RelatedMembershipBooleanFilter,
24
24
  SearchFilter,
25
25
  )
26
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
27
26
  from nautobot.dcim.models import DeviceRedundancyGroup, DeviceType, Location, Platform
28
27
  from nautobot.extras.choices import (
29
28
  JobQueueTypeChoices,
@@ -33,7 +32,18 @@ from nautobot.extras.choices import (
33
32
  SecretsGroupAccessTypeChoices,
34
33
  SecretsGroupSecretTypeChoices,
35
34
  )
36
- from nautobot.extras.filters.customfields import (
35
+ from nautobot.extras.filter_mixins import (
36
+ ConfigContextRoleFilter,
37
+ CreatedUpdatedModelFilterSetMixin,
38
+ CustomFieldModelFilterSetMixin,
39
+ LocalContextModelFilterSetMixin,
40
+ RelationshipFilter,
41
+ RelationshipModelFilterSetMixin,
42
+ RoleModelFilterSetMixin,
43
+ StatusFilter,
44
+ StatusModelFilterSetMixin,
45
+ )
46
+ from nautobot.extras.filter_mixins_customfields import (
37
47
  CustomFieldBooleanFilter,
38
48
  CustomFieldCharFilter,
39
49
  CustomFieldDateFilter,
@@ -45,17 +55,6 @@ from nautobot.extras.filters.customfields import (
45
55
  CustomFieldMultiValueNumberFilter,
46
56
  CustomFieldNumberFilter,
47
57
  )
48
- from nautobot.extras.filters.mixins import (
49
- ConfigContextRoleFilter,
50
- CreatedUpdatedModelFilterSetMixin,
51
- CustomFieldModelFilterSetMixin,
52
- LocalContextModelFilterSetMixin,
53
- RelationshipFilter,
54
- RelationshipModelFilterSetMixin,
55
- RoleModelFilterSetMixin,
56
- StatusFilter,
57
- StatusModelFilterSetMixin,
58
- )
59
58
  from nautobot.extras.models import (
60
59
  ApprovalWorkflow,
61
60
  ApprovalWorkflowDefinition,
@@ -126,14 +125,12 @@ __all__ = (
126
125
  "ContactFilterSet",
127
126
  "ContentTypeFilterSet",
128
127
  "ContentTypeMultipleChoiceFilter",
129
- "CreatedUpdatedFilterSet",
130
128
  "CreatedUpdatedModelFilterSetMixin",
131
129
  "CustomFieldBooleanFilter",
132
130
  "CustomFieldCharFilter",
133
131
  "CustomFieldDateFilter",
134
132
  "CustomFieldFilterMixin",
135
133
  "CustomFieldJSONFilter",
136
- "CustomFieldModelFilterSet",
137
134
  "CustomFieldModelFilterSetMixin",
138
135
  "CustomFieldMultiSelectFilter",
139
136
  "CustomFieldMultiValueCharFilter",
@@ -153,7 +150,6 @@ __all__ = (
153
150
  "JobQueueAssignmentFilterSet",
154
151
  "JobQueueFilterSet",
155
152
  "JobResultFilterSet",
156
- "LocalContextFilterSet",
157
153
  "LocalContextModelFilterSetMixin",
158
154
  "MetadataChoiceFilterSet",
159
155
  "MetadataTypeFilterSet",
@@ -178,17 +174,6 @@ __all__ = (
178
174
  )
179
175
 
180
176
 
181
- # TODO: remove in 2.2
182
- @class_deprecated_in_favor_of(CreatedUpdatedModelFilterSetMixin)
183
- class CreatedUpdatedFilterSet(CreatedUpdatedModelFilterSetMixin):
184
- pass
185
-
186
-
187
- @class_deprecated_in_favor_of(RelationshipModelFilterSetMixin)
188
- class RelationshipModelFilterSet(RelationshipModelFilterSetMixin):
189
- pass
190
-
191
-
192
177
  #
193
178
  # Approval Workflows
194
179
  #
@@ -199,7 +184,7 @@ class ApprovalWorkflowStageDefinitionFilterSet(BaseFilterSet):
199
184
 
200
185
  q = SearchFilter(
201
186
  filter_predicates={
202
- "weight": {
187
+ "sequence": {
203
188
  "lookup_expr": "exact",
204
189
  "preprocessor": int,
205
190
  },
@@ -572,12 +557,6 @@ class ContentTypeFilterSet(BaseFilterSet):
572
557
  return queryset.filter(FeatureQuery(value).get_query())
573
558
 
574
559
 
575
- # TODO: remove in 2.2
576
- @class_deprecated_in_favor_of(CustomFieldModelFilterSetMixin)
577
- class CustomFieldModelFilterSet(CustomFieldModelFilterSetMixin):
578
- pass
579
-
580
-
581
560
  class CustomFieldFilterSet(BaseFilterSet):
582
561
  q = SearchFilter(
583
562
  filter_predicates={
@@ -787,7 +766,7 @@ class CustomLinkFilterSet(BaseFilterSet):
787
766
  #
788
767
 
789
768
  # Must be imported **after* NautobotFilterSet class is defined to avoid a circular import loop.
790
- from nautobot.tenancy.filters.mixins import TenancyModelFilterSetMixin # noqa: E402
769
+ from nautobot.tenancy.filter_mixins import TenancyModelFilterSetMixin # noqa: E402
791
770
 
792
771
 
793
772
  class DynamicGroupFilterSet(TenancyModelFilterSetMixin, NautobotFilterSet):
@@ -1270,17 +1249,6 @@ class JobButtonFilterSet(BaseFilterSet):
1270
1249
  )
1271
1250
 
1272
1251
 
1273
- #
1274
- # Filter for Local Config Context Data
1275
- #
1276
-
1277
-
1278
- # TODO: remove in 2.2
1279
- @class_deprecated_in_favor_of(LocalContextModelFilterSetMixin)
1280
- class LocalContextFilterSet(LocalContextModelFilterSetMixin):
1281
- pass
1282
-
1283
-
1284
1252
  #
1285
1253
  # Metadata
1286
1254
  #
@@ -42,7 +42,6 @@ from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
42
42
  from nautobot.core.forms.fields import MultiValueCharField
43
43
  from nautobot.core.forms.forms import ConfirmationForm
44
44
  from nautobot.core.forms.widgets import ClearableFileInput
45
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
46
45
  from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
47
46
  from nautobot.extras.choices import (
48
47
  ApprovalWorkflowStateChoices,
@@ -153,7 +152,6 @@ __all__ = (
153
152
  "ConfigContextSchemaBulkEditForm",
154
153
  "ConfigContextSchemaFilterForm",
155
154
  "ConfigContextSchemaForm",
156
- "CustomFieldBulkCreateForm", # 2.0 TODO remove this deprecated class
157
155
  "CustomFieldBulkDeleteForm",
158
156
  "CustomFieldBulkEditForm",
159
157
  "CustomFieldChoiceFormSet",
@@ -338,7 +336,7 @@ ApprovalWorkflowStageDefinitionFormSet = inlineformset_factory(
338
336
  extra=5,
339
337
  widgets={
340
338
  "name": forms.TextInput(attrs={"class": "form-control"}),
341
- "weight": forms.NumberInput(attrs={"class": "form-control"}),
339
+ "sequence": forms.NumberInput(attrs={"class": "form-control"}),
342
340
  "min_approvers": forms.NumberInput(attrs={"class": "form-control"}),
343
341
  "denial_message": forms.TextInput(attrs={"class": "form-control"}),
344
342
  "approver_group": forms.Select(attrs={"class": "form-control"}),
@@ -352,8 +350,8 @@ class ApprovalWorkflowStageDefinitionBulkEditForm(TagsBulkEditFormMixin, Nautobo
352
350
  pk = forms.ModelMultipleChoiceField(
353
351
  queryset=ApprovalWorkflowStageDefinition.objects.all(), widget=forms.MultipleHiddenInput
354
352
  )
355
- weight = forms.IntegerField(required=False, label="Weight")
356
- min_approvers = forms.IntegerField(required=False, label="Min Approvers")
353
+ sequence = forms.IntegerField(required=False, label="Sequence")
354
+ min_approvers = forms.IntegerField(required=False, label="Minimum Approvers")
357
355
  denial_message = forms.CharField(required=False, label="Denial Message")
358
356
 
359
357
  class Meta:
@@ -374,8 +372,8 @@ class ApprovalWorkflowStageDefinitionFilterForm(NautobotFilterForm):
374
372
  required=False,
375
373
  label="Approval Workflow Definition",
376
374
  )
377
- weight = forms.IntegerField(required=False, label="Weight")
378
- min_approvers = forms.IntegerField(required=False, label="Min Approvers")
375
+ sequence = forms.IntegerField(required=False, label="Sequence")
376
+ min_approvers = forms.IntegerField(required=False, label="Minimum Approvers")
379
377
  approver_group = DynamicModelChoiceField(
380
378
  queryset=Group.objects.all(),
381
379
  required=False,
@@ -827,12 +825,6 @@ class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelFormMixin):
827
825
  self.custom_fields.append(field_name)
828
826
 
829
827
 
830
- # 2.0 TODO: remove this class
831
- @class_deprecated_in_favor_of(CustomFieldModelBulkEditFormMixin)
832
- class CustomFieldBulkCreateForm(CustomFieldModelBulkEditFormMixin):
833
- """No longer needed as a separate class - use CustomFieldModelBulkEditFormMixin instead."""
834
-
835
-
836
828
  class CustomFieldBulkDeleteForm(ConfirmationForm):
837
829
  def __init__(self, *args, delete_all=False, **kwargs):
838
830
  super().__init__(*args, **kwargs)
@@ -11,7 +11,6 @@ from nautobot.core.forms import (
11
11
  DynamicModelChoiceField,
12
12
  DynamicModelMultipleChoiceField,
13
13
  )
14
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
15
14
  from nautobot.extras.choices import (
16
15
  DynamicGroupTypeChoices,
17
16
  RelationshipSideChoices,
@@ -48,17 +47,10 @@ __all__ = ( # noqa:RUF022
48
47
  "StatusModelBulkEditFormMixin",
49
48
  "StatusModelFilterFormMixin",
50
49
  "TagsBulkEditFormMixin",
51
- # 2.0 TODO: remove the below deprecated aliases
52
- "AddRemoveTagsForm",
53
- "CustomFieldBulkEditForm",
54
- "CustomFieldModelForm",
55
- "RelationshipModelForm",
56
50
  "RoleModelBulkEditFormMixin",
57
51
  "RoleModelFilterFormMixin",
58
52
  "RoleNotRequiredModelFormMixin",
59
53
  "RoleRequiredModelFormMixin",
60
- "StatusBulkEditFormMixin",
61
- "StatusFilterFormMixin",
62
54
  )
63
55
 
64
56
 
@@ -854,36 +846,3 @@ class TagsBulkEditFormMixin(BulkEditForm):
854
846
  query_params={"content_types": self.model._meta.label_lower},
855
847
  required=False,
856
848
  )
857
-
858
-
859
- # 2.2 TODO: Names below are only for backward compatibility with Nautobot 1.3 and earlier. Remove in 2.2
860
-
861
-
862
- @class_deprecated_in_favor_of(TagsBulkEditFormMixin)
863
- class AddRemoveTagsForm(TagsBulkEditFormMixin):
864
- pass
865
-
866
-
867
- @class_deprecated_in_favor_of(CustomFieldModelBulkEditFormMixin)
868
- class CustomFieldBulkEditForm(CustomFieldModelBulkEditFormMixin):
869
- pass
870
-
871
-
872
- @class_deprecated_in_favor_of(CustomFieldModelFormMixin)
873
- class CustomFieldModelForm(CustomFieldModelFormMixin):
874
- pass
875
-
876
-
877
- @class_deprecated_in_favor_of(RelationshipModelFormMixin)
878
- class RelationshipModelForm(RelationshipModelFormMixin):
879
- pass
880
-
881
-
882
- @class_deprecated_in_favor_of(StatusModelBulkEditFormMixin)
883
- class StatusBulkEditFormMixin(StatusModelBulkEditFormMixin):
884
- pass
885
-
886
-
887
- @class_deprecated_in_favor_of(StatusModelFilterFormMixin)
888
- class StatusFilterFormMixin(StatusModelFilterFormMixin):
889
- pass
@@ -11,6 +11,7 @@ from nautobot.dcim import choices as dcim_choices
11
11
  from nautobot.extras import choices as extras_choices
12
12
  from nautobot.ipam import choices as ipam_choices
13
13
  from nautobot.virtualization import choices as vm_choices
14
+ from nautobot.vpn import choices as vpn_choices
14
15
 
15
16
  # List of 2-tuples of (model_path, choiceset)
16
17
  # Add new mappings here as other models are supported.
@@ -36,6 +37,7 @@ STATUS_CHOICESET_MAP = {
36
37
  "ipam.VRF": ipam_choices.VRFStatusChoices,
37
38
  "virtualization.VirtualMachine": vm_choices.VirtualMachineStatusChoices,
38
39
  "virtualization.VMInterface": vm_choices.VMInterfaceStatusChoices,
40
+ "vpn.VPNTunnel": vpn_choices.VPNTunnelStatusChoices,
39
41
  }
40
42
 
41
43
 
@@ -101,6 +103,7 @@ STATUS_DESCRIPTION_MAP = {
101
103
  # Add new mappings here as other models are supported.
102
104
  ROLE_CHOICESET_MAP = {
103
105
  "extras.ContactAssociation": extras_choices.ContactAssociationRoleChoices,
106
+ "vpn.VPNTunnelEndpoint": vpn_choices.VPNTunnelEndpointRoleChoices,
104
107
  }
105
108
 
106
109
  # Map of role name -> default hex_color used when importing color choices in `export_roles_from_choiceset()`.
@@ -110,6 +113,9 @@ ROLE_COLOR_MAP = {
110
113
  "Billing": ColorChoices.COLOR_GREEN,
111
114
  "Support": ColorChoices.COLOR_YELLOW,
112
115
  "On Site": ColorChoices.COLOR_BLACK,
116
+ "Hub": ColorChoices.COLOR_DARK_GREEN,
117
+ "Spoke": ColorChoices.COLOR_LIGHT_GREEN,
118
+ "Peer": ColorChoices.COLOR_ORANGE,
113
119
  }
114
120
 
115
121
  # Map of role name -> description used when importing role choices in `export_roles_from_choiceset()`.
@@ -118,6 +124,9 @@ ROLE_DESCRIPTION_MAP = {
118
124
  "Billing": "Unit plays a billing role",
119
125
  "Support": "Unit plays a support role",
120
126
  "On Site": "Unit plays an on site role",
127
+ "Hub": "Unit plays a Hub role",
128
+ "Spoke": "Unit plays a Spoke role",
129
+ "Peer": "Unit plays a Peer role",
121
130
  }
122
131
 
123
132
 
@@ -68,7 +68,7 @@ class Migration(migrations.Migration):
68
68
  ),
69
69
  ("name", models.CharField(max_length=255, unique=True)),
70
70
  ("model_constraints", models.JSONField(blank=True, default=dict)),
71
- ("priority", models.IntegerField(default=0)),
71
+ ("weight", models.IntegerField(default=0)),
72
72
  (
73
73
  "model_content_type",
74
74
  models.ForeignKey(
@@ -83,7 +83,7 @@ class Migration(migrations.Migration):
83
83
  options={
84
84
  "verbose_name": "Approval Workflow Definition",
85
85
  "ordering": ["name"],
86
- "unique_together": {("model_content_type", "priority")},
86
+ "unique_together": {("model_content_type", "weight")},
87
87
  },
88
88
  bases=(
89
89
  nautobot.extras.models.mixins.DynamicGroupMixin,
@@ -122,7 +122,7 @@ class Migration(migrations.Migration):
122
122
  ],
123
123
  options={
124
124
  "verbose_name": "Approval Workflow Stage",
125
- "ordering": ["approval_workflow", "approval_workflow_stage_definition__weight"],
125
+ "ordering": ["approval_workflow", "approval_workflow_stage_definition__sequence"],
126
126
  },
127
127
  bases=(
128
128
  nautobot.extras.models.mixins.DynamicGroupMixin,
@@ -179,7 +179,7 @@ class Migration(migrations.Migration):
179
179
  "_custom_field_data",
180
180
  models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
181
181
  ),
182
- ("weight", models.PositiveIntegerField()),
182
+ ("sequence", models.PositiveIntegerField()),
183
183
  ("name", models.CharField(max_length=255)),
184
184
  ("min_approvers", models.PositiveIntegerField()),
185
185
  ("denial_message", models.CharField(blank=True, max_length=255)),
@@ -202,10 +202,10 @@ class Migration(migrations.Migration):
202
202
  ],
203
203
  options={
204
204
  "verbose_name": "Approval Workflow Stage Definition",
205
- "ordering": ["approval_workflow_definition", "weight"],
205
+ "ordering": ["approval_workflow_definition", "sequence"],
206
206
  "unique_together": {
207
207
  ("approval_workflow_definition", "name"),
208
- ("approval_workflow_definition", "weight"),
208
+ ("approval_workflow_definition", "sequence"),
209
209
  },
210
210
  },
211
211
  bases=(
@@ -0,0 +1,37 @@
1
+ # Generated by Django 4.2.24 on 2025-10-04 00:03
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("extras", "0128_remove_job_approval_required_and_more"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AddField(
13
+ model_name="jobresult",
14
+ name="debug_log_count",
15
+ field=models.PositiveIntegerField(blank=True, editable=False, null=True),
16
+ ),
17
+ migrations.AddField(
18
+ model_name="jobresult",
19
+ name="error_log_count",
20
+ field=models.PositiveIntegerField(blank=True, editable=False, null=True),
21
+ ),
22
+ migrations.AddField(
23
+ model_name="jobresult",
24
+ name="info_log_count",
25
+ field=models.PositiveIntegerField(blank=True, editable=False, null=True),
26
+ ),
27
+ migrations.AddField(
28
+ model_name="jobresult",
29
+ name="success_log_count",
30
+ field=models.PositiveIntegerField(blank=True, editable=False, null=True),
31
+ ),
32
+ migrations.AddField(
33
+ model_name="jobresult",
34
+ name="warning_log_count",
35
+ field=models.PositiveIntegerField(blank=True, editable=False, null=True),
36
+ ),
37
+ ]
@@ -0,0 +1,42 @@
1
+ """Migration to populate job_results.*_log_counts on existing JobResults."""
2
+
3
+ from django.db import migrations
4
+ from django.db.models import Count, Q
5
+
6
+
7
+ def _populate_log_counts(apps, *_):
8
+ JobResult = apps.get_model("extras", "JobResult")
9
+ job_results_missing_counts = JobResult.objects.filter(
10
+ Q(debug_log_count=None)
11
+ | Q(success_log_count=None)
12
+ | Q(info_log_count=None)
13
+ | Q(warning_log_count=None)
14
+ | Q(error_log_count=None)
15
+ )
16
+ for job_result in job_results_missing_counts:
17
+ db_log_counts = job_result.job_log_entries.aggregate(
18
+ debug_log_count=Count("pk", filter=Q(log_level="debug")),
19
+ success_log_count=Count("pk", filter=Q(log_level="success")),
20
+ info_log_count=Count("pk", filter=Q(log_level="info")),
21
+ warning_log_count=Count("pk", filter=Q(log_level="warning")),
22
+ error_log_count=Count(
23
+ "pk",
24
+ filter=Q(log_level__in=["failure", "error", "critical"]),
25
+ ),
26
+ )
27
+ job_result.debug_log_count = db_log_counts["debug_log_count"]
28
+ job_result.success_log_count = db_log_counts["success_log_count"]
29
+ job_result.info_log_count = db_log_counts["info_log_count"]
30
+ job_result.warning_log_count = db_log_counts["warning_log_count"]
31
+ job_result.error_log_count = db_log_counts["error_log_count"]
32
+ job_result.save()
33
+
34
+
35
+ class Migration(migrations.Migration):
36
+ dependencies = [
37
+ ("extras", "0129_jobresult_debug_log_count_jobresult_error_log_count_and_more"),
38
+ ]
39
+
40
+ operations = [
41
+ migrations.RunPython(code=_populate_log_counts, reverse_code=migrations.RunPython.noop),
42
+ ]
@@ -43,7 +43,7 @@ from .models import (
43
43
  from .relationships import Relationship, RelationshipAssociation, RelationshipModel
44
44
  from .roles import Role, RoleField
45
45
  from .secrets import Secret, SecretsGroup, SecretsGroupAssociation
46
- from .statuses import Status, StatusField, StatusModel
46
+ from .statuses import Status, StatusField
47
47
  from .tags import Tag, TaggedItem
48
48
 
49
49
  __all__ = (
@@ -103,7 +103,6 @@ __all__ = (
103
103
  "StaticGroupAssociation",
104
104
  "Status",
105
105
  "StatusField",
106
- "StatusModel",
107
106
  "Tag",
108
107
  "TaggedItem",
109
108
  "Team",