nautobot 2.3.15b1__py3-none-any.whl → 2.4.0__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 (785) hide show
  1. nautobot/__init__.py +15 -0
  2. nautobot/apps/__init__.py +1 -1
  3. nautobot/apps/api.py +8 -10
  4. nautobot/apps/change_logging.py +2 -2
  5. nautobot/apps/choices.py +4 -4
  6. nautobot/apps/config.py +32 -3
  7. nautobot/apps/events.py +19 -0
  8. nautobot/apps/exceptions.py +0 -2
  9. nautobot/apps/factory.py +2 -2
  10. nautobot/apps/filters.py +1 -1
  11. nautobot/apps/forms.py +20 -20
  12. nautobot/apps/graphql.py +2 -2
  13. nautobot/apps/jobs.py +8 -8
  14. nautobot/apps/models.py +19 -19
  15. nautobot/apps/tables.py +1 -1
  16. nautobot/apps/testing.py +10 -10
  17. nautobot/apps/ui.py +44 -9
  18. nautobot/apps/utils.py +7 -15
  19. nautobot/apps/views.py +8 -6
  20. nautobot/circuits/api/serializers.py +1 -0
  21. nautobot/circuits/api/views.py +4 -8
  22. nautobot/circuits/navigation.py +0 -57
  23. nautobot/circuits/templates/circuits/circuit_create.html +1 -7
  24. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
  25. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
  26. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
  27. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
  28. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
  29. nautobot/circuits/tests/integration/test_relationships.py +33 -24
  30. nautobot/circuits/tests/test_filters.py +4 -8
  31. nautobot/circuits/views.py +143 -26
  32. nautobot/cloud/api/views.py +6 -10
  33. nautobot/cloud/factory.py +4 -1
  34. nautobot/cloud/models.py +1 -1
  35. nautobot/cloud/tests/test_filters.py +5 -4
  36. nautobot/cloud/views.py +0 -16
  37. nautobot/core/api/constants.py +11 -0
  38. nautobot/core/api/fields.py +5 -5
  39. nautobot/core/api/filter_backends.py +3 -9
  40. nautobot/core/api/metadata.py +28 -256
  41. nautobot/core/api/pagination.py +3 -2
  42. nautobot/core/api/renderers.py +3 -0
  43. nautobot/core/api/schema.py +13 -2
  44. nautobot/core/api/serializers.py +54 -268
  45. nautobot/core/api/urls.py +3 -4
  46. nautobot/core/api/utils.py +0 -62
  47. nautobot/core/api/views.py +102 -159
  48. nautobot/core/apps/__init__.py +22 -575
  49. nautobot/core/celery/__init__.py +13 -0
  50. nautobot/core/celery/schedulers.py +48 -3
  51. nautobot/core/choices.py +2 -2
  52. nautobot/core/cli/__init__.py +8 -0
  53. nautobot/core/constants.py +7 -0
  54. nautobot/core/events/__init__.py +116 -0
  55. nautobot/core/events/base.py +27 -0
  56. nautobot/core/events/exceptions.py +10 -0
  57. nautobot/core/events/redis_broker.py +48 -0
  58. nautobot/core/events/syslog_broker.py +19 -0
  59. nautobot/core/exceptions.py +0 -6
  60. nautobot/core/filters.py +19 -16
  61. nautobot/core/forms/__init__.py +19 -19
  62. nautobot/core/forms/fields.py +62 -14
  63. nautobot/core/forms/forms.py +33 -2
  64. nautobot/core/forms/utils.py +2 -1
  65. nautobot/core/graphql/schema.py +3 -1
  66. nautobot/core/graphql/types.py +1 -1
  67. nautobot/core/jobs/__init__.py +28 -7
  68. nautobot/core/jobs/bulk_actions.py +248 -0
  69. nautobot/core/jobs/cleanup.py +2 -2
  70. nautobot/core/jobs/groups.py +1 -1
  71. nautobot/core/management/commands/generate_test_data.py +21 -0
  72. nautobot/core/management/commands/validate_models.py +1 -1
  73. nautobot/core/middleware.py +16 -0
  74. nautobot/core/models/__init__.py +1 -1
  75. nautobot/core/models/fields.py +11 -7
  76. nautobot/core/models/query_functions.py +2 -2
  77. nautobot/core/models/tree_queries.py +2 -2
  78. nautobot/core/settings.py +71 -4
  79. nautobot/core/settings.yaml +107 -0
  80. nautobot/core/tables.py +15 -51
  81. nautobot/core/tasks.py +1 -1
  82. nautobot/core/templates/about.html +67 -0
  83. nautobot/core/templates/components/button/default.html +7 -0
  84. nautobot/core/templates/components/button/dropdown.html +20 -0
  85. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  86. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  87. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  88. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  89. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  90. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  91. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  92. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  93. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  94. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  95. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  96. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  97. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  98. nautobot/core/templates/components/panel/panel.html +16 -0
  99. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  100. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  101. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  102. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  103. nautobot/core/templates/generic/object_retrieve.html +28 -17
  104. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  105. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  106. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  107. nautobot/core/templates/inc/footer.html +1 -0
  108. nautobot/core/templates/inc/media.html +3 -0
  109. nautobot/core/templates/inc/nav_menu.html +2 -1
  110. nautobot/core/templates/inc/relationships_panel.html +1 -1
  111. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  112. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  113. nautobot/core/templates/nautobot_config.py.j2 +16 -0
  114. nautobot/core/templates/panel_table.html +12 -0
  115. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  116. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  117. nautobot/core/templates/utilities/theme_preview.html +7 -0
  118. nautobot/core/templatetags/helpers.py +104 -6
  119. nautobot/core/templatetags/ui_framework.py +40 -0
  120. nautobot/core/testing/__init__.py +8 -8
  121. nautobot/core/testing/api.py +187 -137
  122. nautobot/core/testing/context.py +18 -0
  123. nautobot/core/testing/filters.py +44 -34
  124. nautobot/core/testing/forms.py +2 -0
  125. nautobot/core/testing/views.py +68 -146
  126. nautobot/core/tests/integration/test_app_home.py +0 -1
  127. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  128. nautobot/core/tests/integration/test_filters.py +0 -2
  129. nautobot/core/tests/integration/test_home.py +0 -1
  130. nautobot/core/tests/integration/test_navbar.py +0 -1
  131. nautobot/core/tests/integration/test_view_authentication.py +2 -1
  132. nautobot/core/tests/nautobot_config.py +198 -0
  133. nautobot/core/tests/runner.py +3 -3
  134. nautobot/core/tests/test_api.py +154 -176
  135. nautobot/core/tests/test_events.py +214 -0
  136. nautobot/core/tests/test_forms.py +1 -0
  137. nautobot/core/tests/test_jinja_filters.py +1 -0
  138. nautobot/core/tests/test_jobs.py +387 -14
  139. nautobot/core/tests/test_navigations.py +7 -241
  140. nautobot/core/tests/test_settings_schema.py +7 -0
  141. nautobot/core/tests/test_tables.py +100 -0
  142. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  143. nautobot/core/tests/test_ui.py +150 -0
  144. nautobot/core/tests/test_utils.py +55 -18
  145. nautobot/core/tests/test_views.py +153 -5
  146. nautobot/core/ui/__init__.py +0 -0
  147. nautobot/core/ui/base.py +11 -0
  148. nautobot/core/ui/choices.py +44 -0
  149. nautobot/core/ui/homepage.py +167 -0
  150. nautobot/core/ui/nav.py +280 -0
  151. nautobot/core/ui/object_detail.py +1855 -0
  152. nautobot/core/ui/utils.py +36 -0
  153. nautobot/core/urls.py +15 -0
  154. nautobot/core/utils/config.py +30 -3
  155. nautobot/core/utils/lookup.py +12 -2
  156. nautobot/core/utils/querysets.py +64 -0
  157. nautobot/core/utils/requests.py +24 -9
  158. nautobot/core/views/__init__.py +48 -1
  159. nautobot/core/views/generic.py +82 -177
  160. nautobot/core/views/mixins.py +98 -38
  161. nautobot/core/views/paginator.py +8 -5
  162. nautobot/core/views/renderers.py +9 -9
  163. nautobot/core/views/utils.py +11 -0
  164. nautobot/core/wsgi.py +3 -3
  165. nautobot/dcim/api/serializers.py +82 -189
  166. nautobot/dcim/api/urls.py +5 -0
  167. nautobot/dcim/api/views.py +57 -110
  168. nautobot/dcim/apps.py +1 -0
  169. nautobot/dcim/choices.py +28 -0
  170. nautobot/dcim/factory.py +58 -0
  171. nautobot/dcim/filters/__init__.py +204 -2
  172. nautobot/dcim/forms.py +221 -9
  173. nautobot/dcim/graphql/types.py +2 -2
  174. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  175. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  176. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  177. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  178. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  179. nautobot/dcim/models/__init__.py +5 -1
  180. nautobot/dcim/models/device_component_templates.py +2 -2
  181. nautobot/dcim/models/device_components.py +22 -20
  182. nautobot/dcim/models/devices.py +181 -3
  183. nautobot/dcim/models/locations.py +3 -3
  184. nautobot/dcim/models/power.py +6 -5
  185. nautobot/dcim/models/racks.py +6 -6
  186. nautobot/dcim/navigation.py +25 -224
  187. nautobot/dcim/signals.py +44 -0
  188. nautobot/dcim/tables/__init__.py +5 -3
  189. nautobot/dcim/tables/devices.py +103 -7
  190. nautobot/dcim/tables/devicetypes.py +2 -2
  191. nautobot/dcim/tables/racks.py +1 -1
  192. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  193. nautobot/dcim/templates/dcim/controller_create.html +2 -7
  194. nautobot/dcim/templates/dcim/controller_retrieve.html +6 -10
  195. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  196. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -0
  197. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +51 -0
  198. nautobot/dcim/templates/dcim/device/base.html +6 -42
  199. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  200. nautobot/dcim/templates/dcim/device.html +4 -10
  201. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  202. nautobot/dcim/templates/dcim/interface.html +1 -0
  203. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  204. nautobot/dcim/templates/dcim/location.html +1 -9
  205. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  206. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  207. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  208. nautobot/dcim/templates/dcim/rack.html +1 -9
  209. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  210. nautobot/dcim/templates/dcim/rackreservation.html +1 -9
  211. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +68 -0
  212. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +28 -0
  213. nautobot/dcim/tests/integration/test_controller.py +62 -0
  214. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  215. nautobot/dcim/tests/test_api.py +188 -64
  216. nautobot/dcim/tests/test_filters.py +172 -76
  217. nautobot/dcim/tests/test_graphql.py +52 -0
  218. nautobot/dcim/tests/test_jobs.py +118 -0
  219. nautobot/dcim/tests/test_models.py +159 -5
  220. nautobot/dcim/tests/test_signals.py +1 -0
  221. nautobot/dcim/tests/test_views.py +118 -88
  222. nautobot/dcim/urls.py +72 -27
  223. nautobot/dcim/utils.py +2 -2
  224. nautobot/dcim/views.py +357 -62
  225. nautobot/extras/api/customfields.py +2 -2
  226. nautobot/extras/api/serializers.py +111 -87
  227. nautobot/extras/api/urls.py +4 -0
  228. nautobot/extras/api/views.py +93 -35
  229. nautobot/extras/choices.py +13 -0
  230. nautobot/extras/constants.py +2 -1
  231. nautobot/extras/context_managers.py +23 -6
  232. nautobot/extras/datasources/git.py +4 -1
  233. nautobot/extras/factory.py +27 -0
  234. nautobot/extras/filters/__init__.py +66 -5
  235. nautobot/extras/forms/base.py +2 -2
  236. nautobot/extras/forms/forms.py +262 -59
  237. nautobot/extras/forms/mixins.py +3 -3
  238. nautobot/extras/graphql/types.py +25 -1
  239. nautobot/extras/health_checks.py +1 -2
  240. nautobot/extras/jobs.py +114 -20
  241. nautobot/extras/management/__init__.py +1 -0
  242. nautobot/extras/management/commands/runjob.py +7 -79
  243. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  244. nautobot/extras/management/utils.py +87 -0
  245. nautobot/extras/managers.py +3 -1
  246. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  247. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  248. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  249. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  250. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  251. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  252. nautobot/extras/models/__init__.py +5 -1
  253. nautobot/extras/models/change_logging.py +7 -3
  254. nautobot/extras/models/contacts.py +1 -1
  255. nautobot/extras/models/customfields.py +12 -11
  256. nautobot/extras/models/groups.py +11 -9
  257. nautobot/extras/models/jobs.py +237 -37
  258. nautobot/extras/models/models.py +2 -2
  259. nautobot/extras/models/relationships.py +69 -1
  260. nautobot/extras/models/secrets.py +5 -0
  261. nautobot/extras/navigation.py +20 -262
  262. nautobot/extras/plugins/__init__.py +54 -19
  263. nautobot/extras/plugins/marketplace_manifest.yml +455 -0
  264. nautobot/extras/plugins/tables.py +16 -14
  265. nautobot/extras/plugins/urls.py +1 -0
  266. nautobot/extras/plugins/views.py +104 -61
  267. nautobot/extras/registry.py +1 -1
  268. nautobot/extras/secrets/__init__.py +2 -2
  269. nautobot/extras/signals.py +39 -1
  270. nautobot/extras/tables.py +42 -6
  271. nautobot/extras/templates/extras/dynamicgroup.html +1 -9
  272. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  273. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  274. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  275. nautobot/extras/templates/extras/job_detail.html +52 -6
  276. nautobot/extras/templates/extras/job_edit.html +6 -2
  277. nautobot/extras/templates/extras/job_list.html +2 -7
  278. nautobot/extras/templates/extras/jobqueue_retrieve.html +36 -0
  279. nautobot/extras/templates/extras/marketplace.html +296 -0
  280. nautobot/extras/templates/extras/plugin_detail.html +32 -15
  281. nautobot/extras/templates/extras/plugins_list.html +35 -1
  282. nautobot/extras/templates/extras/plugins_tiles.html +90 -0
  283. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  284. nautobot/extras/templates/extras/secret.html +0 -65
  285. nautobot/extras/templates/extras/secret_check.js +16 -0
  286. nautobot/extras/templates/extras/secret_create.html +114 -0
  287. nautobot/extras/templates/extras/secret_edit.html +1 -114
  288. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  289. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  290. nautobot/extras/templatetags/job_buttons.py +5 -4
  291. nautobot/extras/templatetags/plugins.py +69 -6
  292. nautobot/extras/test_jobs/api_test_job.py +1 -1
  293. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  294. nautobot/extras/test_jobs/dry_run.py +1 -1
  295. nautobot/extras/test_jobs/fail.py +5 -5
  296. nautobot/extras/test_jobs/file_output.py +1 -1
  297. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  298. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  299. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  300. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  301. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  302. nautobot/extras/test_jobs/log_redaction.py +1 -1
  303. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  304. nautobot/extras/test_jobs/modify_db.py +1 -1
  305. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  306. nautobot/extras/test_jobs/object_var_required.py +1 -1
  307. nautobot/extras/test_jobs/object_vars.py +1 -1
  308. nautobot/extras/test_jobs/pass.py +3 -3
  309. nautobot/extras/test_jobs/profiling.py +1 -1
  310. nautobot/extras/test_jobs/relative_import.py +3 -3
  311. nautobot/extras/test_jobs/singleton.py +16 -0
  312. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  313. nautobot/extras/test_jobs/task_queues.py +1 -1
  314. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  315. nautobot/extras/tests/test_api.py +157 -55
  316. nautobot/extras/tests/test_context_managers.py +4 -1
  317. nautobot/extras/tests/test_customfields.py +1 -1
  318. nautobot/extras/tests/test_datasources.py +2 -1
  319. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  320. nautobot/extras/tests/test_filters.py +219 -535
  321. nautobot/extras/tests/test_forms.py +20 -1
  322. nautobot/extras/tests/test_job_variables.py +73 -152
  323. nautobot/extras/tests/test_jobs.py +192 -62
  324. nautobot/extras/tests/test_models.py +71 -16
  325. nautobot/extras/tests/test_plugins.py +62 -9
  326. nautobot/extras/tests/test_relationships.py +124 -10
  327. nautobot/extras/tests/test_utils.py +23 -2
  328. nautobot/extras/tests/test_views.py +162 -161
  329. nautobot/extras/tests/test_webhooks.py +2 -1
  330. nautobot/extras/urls.py +2 -20
  331. nautobot/extras/utils.py +119 -4
  332. nautobot/extras/views.py +188 -141
  333. nautobot/extras/webhooks.py +5 -2
  334. nautobot/ipam/api/fields.py +3 -3
  335. nautobot/ipam/api/serializers.py +44 -137
  336. nautobot/ipam/api/views.py +68 -110
  337. nautobot/ipam/factory.py +1 -1
  338. nautobot/ipam/filters.py +3 -2
  339. nautobot/ipam/models.py +10 -12
  340. nautobot/ipam/navigation.py +0 -90
  341. nautobot/ipam/querysets.py +2 -2
  342. nautobot/ipam/tables.py +3 -1
  343. nautobot/ipam/templates/ipam/ipaddress.html +1 -9
  344. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  345. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  346. nautobot/ipam/templates/ipam/prefix.html +1 -9
  347. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  348. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  349. nautobot/ipam/templates/ipam/vlan.html +1 -9
  350. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  351. nautobot/ipam/templates/ipam/vrf.html +0 -47
  352. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  353. nautobot/ipam/tests/test_api.py +19 -6
  354. nautobot/ipam/tests/test_filters.py +39 -119
  355. nautobot/ipam/tests/test_forms.py +49 -47
  356. nautobot/ipam/tests/test_migrations.py +30 -30
  357. nautobot/ipam/tests/test_models.py +56 -36
  358. nautobot/ipam/tests/test_querysets.py +14 -0
  359. nautobot/ipam/tests/test_views.py +3 -0
  360. nautobot/ipam/urls.py +3 -69
  361. nautobot/ipam/utils/__init__.py +16 -10
  362. nautobot/ipam/views.py +91 -162
  363. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  364. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  365. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  366. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  367. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  368. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  369. nautobot/project-static/css/base.css +38 -3
  370. nautobot/project-static/docs/404.html +463 -19
  371. nautobot/project-static/docs/apps/index.html +463 -19
  372. nautobot/project-static/docs/apps/nautobot-apps.html +464 -21
  373. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  374. nautobot/project-static/docs/assets/extra.css +5 -1
  375. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
  376. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
  377. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +479 -25
  378. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +476 -22
  379. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +792 -291
  380. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +507 -33
  381. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +512 -36
  382. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +473 -22
  383. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +469 -20
  384. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +499 -35
  385. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9883 -0
  386. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +525 -77
  387. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +548 -53
  388. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +672 -96
  389. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1033 -180
  390. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +526 -51
  391. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +876 -190
  392. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +957 -237
  393. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +477 -23
  394. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +488 -30
  395. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +663 -101
  396. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +949 -481
  397. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6427 -1236
  398. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +476 -22
  399. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +879 -346
  400. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +830 -173
  401. nautobot/project-static/docs/development/apps/api/configuration-view.html +463 -19
  402. nautobot/project-static/docs/development/apps/api/database-backend-config.html +463 -19
  403. nautobot/project-static/docs/development/apps/api/models/django-admin.html +463 -19
  404. nautobot/project-static/docs/development/apps/api/models/global-search.html +463 -19
  405. nautobot/project-static/docs/development/apps/api/models/graphql.html +463 -19
  406. nautobot/project-static/docs/development/apps/api/models/index.html +463 -19
  407. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +463 -19
  408. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +463 -19
  409. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +463 -19
  410. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +463 -19
  411. nautobot/project-static/docs/development/apps/api/platform-features/index.html +463 -19
  412. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +463 -19
  413. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +463 -19
  414. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +463 -19
  415. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +463 -19
  416. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +463 -19
  417. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +463 -19
  418. nautobot/project-static/docs/development/apps/api/prometheus.html +463 -19
  419. nautobot/project-static/docs/development/apps/api/setup.html +467 -155
  420. nautobot/project-static/docs/development/apps/api/testing.html +463 -19
  421. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +463 -19
  422. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +463 -19
  423. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +463 -19
  424. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +463 -19
  425. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +743 -130
  426. nautobot/project-static/docs/development/apps/api/views/base-template.html +463 -19
  427. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +463 -19
  428. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +463 -19
  429. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +463 -19
  430. nautobot/project-static/docs/development/apps/api/views/index.html +465 -20
  431. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +467 -19
  432. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +493 -19
  433. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +463 -19
  434. nautobot/project-static/docs/development/apps/api/views/notes.html +463 -19
  435. nautobot/project-static/docs/development/apps/api/views/rest-api.html +469 -21
  436. nautobot/project-static/docs/development/apps/api/views/urls.html +463 -19
  437. nautobot/project-static/docs/development/apps/index.html +463 -19
  438. nautobot/project-static/docs/development/apps/migration/code-updates.html +464 -52
  439. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +464 -20
  440. nautobot/project-static/docs/development/apps/migration/from-v1.html +463 -19
  441. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +463 -19
  442. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +463 -19
  443. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +463 -19
  444. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +466 -22
  445. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9261 -0
  446. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9375 -0
  447. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9671 -0
  448. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9559 -0
  449. nautobot/project-static/docs/development/apps/porting-from-netbox.html +466 -22
  450. nautobot/project-static/docs/development/core/application-registry.html +463 -19
  451. nautobot/project-static/docs/development/core/best-practices.html +463 -19
  452. nautobot/project-static/docs/development/core/bootstrap-ui.html +463 -19
  453. nautobot/project-static/docs/development/core/caching.html +463 -19
  454. nautobot/project-static/docs/development/core/controllers.html +465 -19
  455. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +491 -90
  456. nautobot/project-static/docs/development/core/generic-views.html +463 -19
  457. nautobot/project-static/docs/development/core/getting-started.html +541 -129
  458. nautobot/project-static/docs/development/core/homepage.html +474 -30
  459. nautobot/project-static/docs/development/core/index.html +463 -19
  460. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9754 -0
  461. nautobot/project-static/docs/development/core/model-checklist.html +473 -27
  462. nautobot/project-static/docs/development/core/model-features.html +463 -19
  463. nautobot/project-static/docs/development/core/natural-keys.html +463 -19
  464. nautobot/project-static/docs/development/core/navigation-menu.html +480 -26
  465. nautobot/project-static/docs/development/core/release-checklist.html +480 -48
  466. nautobot/project-static/docs/development/core/role-internals.html +463 -19
  467. nautobot/project-static/docs/development/core/settings.html +463 -19
  468. nautobot/project-static/docs/development/core/style-guide.html +466 -22
  469. nautobot/project-static/docs/development/core/templates.html +473 -22
  470. nautobot/project-static/docs/development/core/testing.html +463 -19
  471. nautobot/project-static/docs/development/core/ui-component-framework.html +11116 -0
  472. nautobot/project-static/docs/development/core/user-preferences.html +466 -22
  473. nautobot/project-static/docs/development/index.html +463 -19
  474. nautobot/project-static/docs/development/jobs/index.html +501 -21
  475. nautobot/project-static/docs/development/jobs/migration/from-v1.html +463 -19
  476. nautobot/project-static/docs/index.html +471 -38
  477. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  478. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  479. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  480. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  481. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  482. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  483. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  484. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  485. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  486. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  487. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  488. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  489. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  490. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  491. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  492. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  493. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  494. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  495. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  496. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  497. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  498. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  499. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  500. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  501. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  502. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  503. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  504. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  505. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  506. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  507. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  508. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  509. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  510. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  511. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  512. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  513. nautobot/project-static/docs/objects.inv +0 -0
  514. nautobot/project-static/docs/overview/application_stack.html +469 -23
  515. nautobot/project-static/docs/overview/design_philosophy.html +463 -19
  516. nautobot/project-static/docs/release-notes/index.html +485 -22
  517. nautobot/project-static/docs/release-notes/version-1.0.html +651 -208
  518. nautobot/project-static/docs/release-notes/version-1.1.html +648 -205
  519. nautobot/project-static/docs/release-notes/version-1.2.html +723 -280
  520. nautobot/project-static/docs/release-notes/version-1.3.html +749 -306
  521. nautobot/project-static/docs/release-notes/version-1.4.html +834 -392
  522. nautobot/project-static/docs/release-notes/version-1.5.html +1022 -581
  523. nautobot/project-static/docs/release-notes/version-1.6.html +942 -518
  524. nautobot/project-static/docs/release-notes/version-2.0.html +945 -504
  525. nautobot/project-static/docs/release-notes/version-2.1.html +780 -339
  526. nautobot/project-static/docs/release-notes/version-2.2.html +773 -332
  527. nautobot/project-static/docs/release-notes/version-2.3.html +1142 -448
  528. nautobot/project-static/docs/release-notes/version-2.4.html +10323 -0
  529. nautobot/project-static/docs/requirements.txt +2 -2
  530. nautobot/project-static/docs/search/search_index.json +1 -1
  531. nautobot/project-static/docs/sitemap.xml +342 -270
  532. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  533. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +463 -19
  534. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +463 -19
  535. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +463 -19
  536. nautobot/project-static/docs/user-guide/administration/configuration/index.html +475 -32
  537. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +463 -19
  538. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +894 -180
  539. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +463 -19
  540. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +463 -19
  541. nautobot/project-static/docs/user-guide/administration/guides/docker.html +476 -29
  542. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +463 -19
  543. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +463 -19
  544. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +463 -19
  545. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +463 -19
  546. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +463 -19
  547. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +465 -21
  548. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +463 -19
  549. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +463 -19
  550. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +463 -19
  551. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +483 -23
  552. nautobot/project-static/docs/user-guide/administration/installation/index.html +468 -20
  553. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +464 -20
  554. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +463 -19
  555. nautobot/project-static/docs/user-guide/administration/installation/services.html +463 -19
  556. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +463 -19
  557. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +484 -41
  558. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +477 -66
  559. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +477 -66
  560. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +463 -19
  561. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +463 -19
  562. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +463 -19
  563. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +463 -19
  564. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +463 -19
  565. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +466 -23
  566. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +463 -19
  567. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  568. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +463 -19
  569. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +469 -21
  570. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +463 -19
  571. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +463 -19
  572. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +463 -19
  573. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +463 -19
  574. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +463 -19
  575. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +463 -19
  576. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +463 -19
  577. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +463 -19
  578. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +463 -19
  579. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +463 -19
  580. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +463 -19
  581. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +463 -19
  582. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +463 -19
  583. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +463 -19
  584. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +463 -19
  585. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +463 -19
  586. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +463 -19
  587. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +499 -20
  588. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +489 -22
  589. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +463 -19
  590. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +463 -19
  591. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +463 -19
  592. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +463 -19
  593. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +463 -19
  594. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +463 -19
  595. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +463 -19
  596. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +463 -19
  597. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +463 -19
  598. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +463 -19
  599. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +463 -19
  600. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +463 -19
  601. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +463 -19
  602. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +463 -19
  603. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +463 -19
  604. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +463 -19
  605. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +463 -19
  606. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +463 -19
  607. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +463 -19
  608. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +463 -19
  609. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +463 -19
  610. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +463 -19
  611. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +463 -19
  612. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +463 -19
  613. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +463 -19
  614. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +463 -19
  615. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +463 -19
  616. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +463 -19
  617. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +463 -19
  618. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +463 -19
  619. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +463 -19
  620. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +463 -19
  621. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +463 -19
  622. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +463 -19
  623. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9375 -0
  624. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +470 -30
  625. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +463 -19
  626. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +463 -19
  627. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +463 -19
  628. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +463 -19
  629. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +463 -19
  630. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +463 -19
  631. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +463 -19
  632. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +463 -19
  633. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +463 -19
  634. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +463 -19
  635. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +463 -19
  636. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +463 -19
  637. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +466 -22
  638. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +463 -19
  639. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +463 -19
  640. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +463 -19
  641. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +463 -19
  642. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +463 -19
  643. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +463 -19
  644. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +466 -22
  645. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9313 -0
  646. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9217 -0
  647. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9211 -0
  648. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9277 -0
  649. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +463 -19
  650. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +463 -19
  651. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +463 -19
  652. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +463 -19
  653. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +463 -19
  654. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +463 -19
  655. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +463 -19
  656. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +463 -19
  657. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +463 -19
  658. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +463 -19
  659. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +463 -19
  660. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +468 -22
  661. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +463 -19
  662. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  663. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  664. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  665. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  666. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  667. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  668. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  669. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  670. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  671. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  672. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  673. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  674. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  675. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +463 -19
  676. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +463 -19
  677. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +466 -22
  678. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
  679. nautobot/project-static/docs/user-guide/index.html +463 -19
  680. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +466 -22
  681. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +467 -23
  682. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +463 -19
  683. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +463 -19
  684. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +463 -19
  685. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9617 -0
  686. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +466 -22
  687. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +463 -19
  688. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +463 -19
  689. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +463 -19
  690. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +463 -19
  691. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +463 -19
  692. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +472 -23
  693. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +466 -22
  694. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +466 -22
  695. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +463 -19
  696. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9224 -0
  697. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
  698. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +466 -22
  699. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +463 -19
  700. nautobot/project-static/docs/user-guide/platform-functionality/note.html +463 -19
  701. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +463 -19
  702. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +467 -23
  703. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9292 -0
  704. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +463 -19
  705. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +511 -40
  706. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +494 -23
  707. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +463 -19
  708. nautobot/project-static/docs/user-guide/platform-functionality/role.html +463 -19
  709. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +463 -19
  710. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +463 -19
  711. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +466 -22
  712. nautobot/project-static/docs/user-guide/platform-functionality/status.html +463 -19
  713. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +463 -19
  714. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +531 -56
  715. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +463 -19
  716. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +463 -19
  717. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +463 -19
  718. nautobot/project-static/img/jinja_logo.svg +97 -0
  719. nautobot/project-static/js/forms.js +6 -1
  720. nautobot/project-static/js/nav_menu.js +2 -1
  721. nautobot/tenancy/api/serializers.py +0 -2
  722. nautobot/tenancy/api/views.py +9 -13
  723. nautobot/tenancy/factory.py +1 -1
  724. nautobot/tenancy/navigation.py +0 -29
  725. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  726. nautobot/tenancy/tests/test_filters.py +29 -134
  727. nautobot/tenancy/views.py +35 -24
  728. nautobot/users/admin.py +3 -1
  729. nautobot/users/api/serializers.py +4 -4
  730. nautobot/users/api/views.py +3 -3
  731. nautobot/users/forms.py +19 -0
  732. nautobot/users/templates/users/preferences.html +22 -0
  733. nautobot/users/tests/test_filters.py +1 -19
  734. nautobot/users/tests/test_views.py +57 -0
  735. nautobot/users/utils.py +8 -0
  736. nautobot/users/views.py +48 -11
  737. nautobot/virtualization/api/serializers.py +4 -4
  738. nautobot/virtualization/api/views.py +5 -24
  739. nautobot/virtualization/filters.py +1 -2
  740. nautobot/virtualization/models.py +1 -1
  741. nautobot/virtualization/navigation.py +0 -48
  742. nautobot/virtualization/tables.py +2 -2
  743. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  744. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  745. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
  746. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  747. nautobot/virtualization/tests/test_filters.py +57 -166
  748. nautobot/virtualization/views.py +18 -15
  749. nautobot/wireless/__init__.py +0 -0
  750. nautobot/wireless/api/__init__.py +0 -0
  751. nautobot/wireless/api/serializers.py +44 -0
  752. nautobot/wireless/api/urls.py +20 -0
  753. nautobot/wireless/api/views.py +34 -0
  754. nautobot/wireless/apps.py +8 -0
  755. nautobot/wireless/choices.py +345 -0
  756. nautobot/wireless/factory.py +138 -0
  757. nautobot/wireless/filters.py +167 -0
  758. nautobot/wireless/forms.py +283 -0
  759. nautobot/wireless/homepage.py +19 -0
  760. nautobot/wireless/migrations/0001_initial.py +223 -0
  761. nautobot/wireless/migrations/__init__.py +0 -0
  762. nautobot/wireless/models.py +207 -0
  763. nautobot/wireless/navigation.py +105 -0
  764. nautobot/wireless/tables.py +244 -0
  765. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  766. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  767. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  768. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +56 -0
  769. nautobot/wireless/tests/__init__.py +0 -0
  770. nautobot/wireless/tests/integration/__init__.py +0 -0
  771. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  772. nautobot/wireless/tests/test_api.py +247 -0
  773. nautobot/wireless/tests/test_filters.py +82 -0
  774. nautobot/wireless/tests/test_models.py +22 -0
  775. nautobot/wireless/tests/test_views.py +378 -0
  776. nautobot/wireless/urls.py +13 -0
  777. nautobot/wireless/views.py +119 -0
  778. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/METADATA +11 -13
  779. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/RECORD +783 -613
  780. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/WHEEL +1 -1
  781. nautobot/core/fixtures/user-data.json +0 -59
  782. nautobot/core/utils/navigation.py +0 -54
  783. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/LICENSE.txt +0 -0
  784. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/NOTICE +0 -0
  785. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/entry_points.txt +0 -0
nautobot/extras/views.py CHANGED
@@ -1,8 +1,6 @@
1
1
  import logging
2
2
  from urllib.parse import parse_qs
3
3
 
4
- from celery import chain
5
- from django.conf import settings
6
4
  from django.contrib import messages
7
5
  from django.contrib.contenttypes.models import ContentType
8
6
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
@@ -18,21 +16,21 @@ from django.utils import timezone
18
16
  from django.utils.encoding import iri_to_uri
19
17
  from django.utils.html import format_html
20
18
  from django.utils.http import url_has_allowed_host_and_scheme
19
+ from django.utils.timezone import get_current_timezone
21
20
  from django.views.generic import View
22
21
  from django_tables2 import RequestConfig
23
22
  from jsonschema.validators import Draft7Validator
24
23
  from rest_framework.decorators import action
25
24
  from rest_framework.permissions import IsAuthenticated
26
25
 
27
- try:
28
- from zoneinfo import ZoneInfo
29
- except ImportError: # python 3.8
30
- from backports.zoneinfo import ZoneInfo
31
-
26
+ from nautobot.core.constants import PAGINATE_COUNT_DEFAULT
27
+ from nautobot.core.events import publish_event
32
28
  from nautobot.core.forms import restrict_form_fields
33
29
  from nautobot.core.models.querysets import count_related
34
- from nautobot.core.models.utils import pretty_print_query
30
+ from nautobot.core.models.utils import pretty_print_query, serialize_object_v2
35
31
  from nautobot.core.tables import ButtonsColumn
32
+ from nautobot.core.ui import object_detail
33
+ from nautobot.core.ui.choices import SectionChoices
36
34
  from nautobot.core.utils.config import get_settings_or_config
37
35
  from nautobot.core.utils.lookup import (
38
36
  get_filterset_for_model,
@@ -45,6 +43,7 @@ from nautobot.core.utils.requests import normalize_querydict
45
43
  from nautobot.core.views import generic, viewsets
46
44
  from nautobot.core.views.mixins import (
47
45
  GetReturnURLMixin,
46
+ ObjectBulkCreateViewMixin,
48
47
  ObjectBulkDestroyViewMixin,
49
48
  ObjectBulkUpdateViewMixin,
50
49
  ObjectChangeLogViewMixin,
@@ -52,18 +51,23 @@ from nautobot.core.views.mixins import (
52
51
  ObjectDetailViewMixin,
53
52
  ObjectEditViewMixin,
54
53
  ObjectListViewMixin,
54
+ ObjectNotesViewMixin,
55
55
  ObjectPermissionRequiredMixin,
56
56
  )
57
57
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
58
58
  from nautobot.core.views.utils import prepare_cloned_fields
59
59
  from nautobot.core.views.viewsets import NautobotUIViewSet
60
- from nautobot.dcim.models import Controller, Device, Interface, Module, Rack
61
- from nautobot.dcim.tables import ControllerTable, DeviceTable, InterfaceTable, ModuleTable, RackTable
62
- from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
60
+ from nautobot.dcim.models import Controller, Device, Interface, Module, Rack, VirtualDeviceContext
61
+ from nautobot.dcim.tables import (
62
+ ControllerTable,
63
+ DeviceTable,
64
+ InterfaceTable,
65
+ ModuleTable,
66
+ RackTable,
67
+ VirtualDeviceContextTable,
68
+ )
63
69
  from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
64
- from nautobot.extras.signals import change_context_state
65
- from nautobot.extras.tasks import delete_custom_field_data
66
- from nautobot.extras.utils import get_base_template, get_worker_count
70
+ from nautobot.extras.utils import get_base_template, get_job_queue, get_worker_count
67
71
  from nautobot.ipam.models import IPAddress, Prefix, VLAN
68
72
  from nautobot.ipam.tables import IPAddressTable, PrefixTable, VLANTable
69
73
  from nautobot.virtualization.models import VirtualMachine, VMInterface
@@ -71,7 +75,13 @@ from nautobot.virtualization.tables import VirtualMachineTable, VMInterfaceTable
71
75
 
72
76
  from . import filters, forms, tables
73
77
  from .api import serializers
74
- from .choices import DynamicGroupTypeChoices, JobExecutionType, JobResultStatusChoices, LogLevelChoices
78
+ from .choices import (
79
+ DynamicGroupTypeChoices,
80
+ JobExecutionType,
81
+ JobQueueTypeChoices,
82
+ JobResultStatusChoices,
83
+ LogLevelChoices,
84
+ )
75
85
  from .datasources import (
76
86
  enqueue_git_repository_diff_origin_and_local,
77
87
  enqueue_pull_git_repository_and_refresh_data,
@@ -96,6 +106,7 @@ from .models import (
96
106
  JobButton,
97
107
  JobHook,
98
108
  JobLogEntry,
109
+ JobQueue,
99
110
  JobResult,
100
111
  MetadataType,
101
112
  Note,
@@ -643,37 +654,7 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView):
643
654
  queryset = CustomField.objects.all()
644
655
  table = tables.CustomFieldTable
645
656
  filterset = filters.CustomFieldFilterSet
646
-
647
- def construct_custom_field_delete_tasks(self, queryset):
648
- """
649
- Helper method to construct a list of celery tasks to execute when bulk deleting custom fields.
650
- """
651
- change_context = change_context_state.get()
652
- if change_context is None:
653
- context = None
654
- else:
655
- context = change_context.as_dict(queryset)
656
- context["context_detail"] = "bulk delete custom field data"
657
- tasks = [
658
- delete_custom_field_data.si(obj.key, set(obj.content_types.values_list("pk", flat=True)), context)
659
- for obj in queryset
660
- ]
661
- return tasks
662
-
663
- def perform_pre_delete(self, request, queryset):
664
- """
665
- Remove all Custom Field Keys/Values from _custom_field_data of the related ContentType in the background.
666
- """
667
- if not get_worker_count():
668
- messages.error(
669
- request, "Celery worker process not running. Object custom fields may fail to reflect this deletion."
670
- )
671
- return
672
- tasks = self.construct_custom_field_delete_tasks(queryset)
673
- # Executing the tasks in the background sequentially using chain() aligns with how a single
674
- # CustomField object is deleted. We decided to not check the result because it needs at least one worker
675
- # to be active and comes with extra performance penalty.
676
- chain(*tasks).apply_async()
657
+ form = forms.CustomFieldBulkDeleteForm
677
658
 
678
659
 
679
660
  #
@@ -1006,6 +987,27 @@ class ExternalIntegrationUIViewSet(NautobotUIViewSet):
1006
987
  serializer_class = serializers.ExternalIntegrationSerializer
1007
988
  table_class = tables.ExternalIntegrationTable
1008
989
 
990
+ object_detail_content = object_detail.ObjectDetailContent(
991
+ panels=(
992
+ object_detail.ObjectFieldsPanel(
993
+ section=SectionChoices.LEFT_HALF,
994
+ weight=100,
995
+ # Default ordering with __all__ leaves something to be desired
996
+ fields=[
997
+ "name",
998
+ "remote_url",
999
+ "http_method",
1000
+ "headers",
1001
+ "verify_ssl",
1002
+ "ca_file_path",
1003
+ "secrets_group",
1004
+ "timeout",
1005
+ "extra_config",
1006
+ ],
1007
+ ),
1008
+ ),
1009
+ )
1010
+
1009
1011
 
1010
1012
  #
1011
1013
  # Git repositories
@@ -1060,10 +1062,10 @@ class GitRepositoryEditView(generic.ObjectEditView):
1060
1062
  obj.user = request.user
1061
1063
  return super().alter_obj(obj, request, url_args, url_kwargs)
1062
1064
 
1063
- def get_return_url(self, request, obj):
1065
+ def get_return_url(self, request, obj=None, default_return_url=None):
1064
1066
  if request.method == "POST":
1065
1067
  return reverse("extras:gitrepository_result", kwargs={"pk": obj.pk})
1066
- return super().get_return_url(request, obj)
1068
+ return super().get_return_url(request, obj=obj, default_return_url=default_return_url)
1067
1069
 
1068
1070
 
1069
1071
  class GitRepositoryDeleteView(generic.ObjectDeleteView):
@@ -1211,27 +1213,27 @@ class ImageAttachmentEditView(generic.ObjectEditView):
1211
1213
  return get_object_or_404(self.queryset, pk=kwargs["pk"])
1212
1214
  return self.queryset.model()
1213
1215
 
1214
- def alter_obj(self, imageattachment, request, args, kwargs):
1215
- if not imageattachment.present_in_database:
1216
+ def alter_obj(self, obj, request, url_args, url_kwargs):
1217
+ if not obj.present_in_database:
1216
1218
  # Assign the parent object based on URL kwargs
1217
- model = kwargs.get("model")
1218
- if "object_id" in kwargs:
1219
- imageattachment.parent = get_object_or_404(model, pk=kwargs["object_id"])
1220
- elif "slug" in kwargs:
1221
- imageattachment.parent = get_object_or_404(model, slug=kwargs["slug"])
1219
+ model = url_kwargs.get("model")
1220
+ if "object_id" in url_kwargs:
1221
+ obj.parent = get_object_or_404(model, pk=url_kwargs["object_id"])
1222
+ elif "slug" in url_kwargs:
1223
+ obj.parent = get_object_or_404(model, slug=url_kwargs["slug"])
1222
1224
  else:
1223
1225
  raise RuntimeError("Neither object_id nor slug were provided?")
1224
- return imageattachment
1226
+ return obj
1225
1227
 
1226
- def get_return_url(self, request, imageattachment):
1227
- return imageattachment.parent.get_absolute_url()
1228
+ def get_return_url(self, request, obj=None, default_return_url=None):
1229
+ return obj.parent.get_absolute_url()
1228
1230
 
1229
1231
 
1230
1232
  class ImageAttachmentDeleteView(generic.ObjectDeleteView):
1231
1233
  queryset = ImageAttachment.objects.all()
1232
1234
 
1233
- def get_return_url(self, request, imageattachment):
1234
- return imageattachment.parent.get_absolute_url()
1235
+ def get_return_url(self, request, obj=None, default_return_url=None):
1236
+ return obj.parent.get_absolute_url()
1235
1237
 
1236
1238
 
1237
1239
  #
@@ -1319,10 +1321,18 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1319
1321
  # for example "?kwargs_from_job_result=<UUID>&integervar=22"
1320
1322
  explicit_initial = initial
1321
1323
  initial = job_result.task_kwargs.copy()
1322
- task_queue = job_result.celery_kwargs.get("queue", None)
1323
- if task_queue is not None:
1324
- initial["_task_queue"] = task_queue
1324
+ job_queue = job_result.celery_kwargs.get("queue", None)
1325
+ jq = None
1326
+ if job_queue is not None:
1327
+ try:
1328
+ jq = JobQueue.objects.get(name=job_queue, queue_type=JobQueueTypeChoices.TYPE_CELERY)
1329
+ except JobQueue.DoesNotExist:
1330
+ pass
1331
+ initial["_job_queue"] = jq
1325
1332
  initial["_profile"] = job_result.celery_kwargs.get("nautobot_job_profile", False)
1333
+ initial["_ignore_singleton_lock"] = job_result.celery_kwargs.get(
1334
+ "nautobot_job_ignore_singleton_lock", False
1335
+ )
1326
1336
  initial.update(explicit_initial)
1327
1337
  except JobResult.DoesNotExist:
1328
1338
  messages.warning(
@@ -1337,7 +1347,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1337
1347
  get_template(job_class.template_name)
1338
1348
  template_name = job_class.template_name
1339
1349
  except TemplateDoesNotExist as err:
1340
- messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1350
+ messages.error(
1351
+ request, f'Unable to render requested custom job template "{job_class.template_name}": {err}'
1352
+ )
1341
1353
  except RuntimeError as err:
1342
1354
  messages.error(request, f"Unable to run or schedule '{job_model}': {err}")
1343
1355
  return redirect("extras:job_list")
@@ -1360,7 +1372,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1360
1372
  job_class = get_job(job_model.class_path, reload=True)
1361
1373
  job_form = job_class.as_form(request.POST, request.FILES) if job_class is not None else None
1362
1374
  schedule_form = forms.JobScheduleForm(request.POST)
1363
- task_queue = request.POST.get("_task_queue")
1375
+ job_queue = request.POST.get("_job_queue")
1364
1376
 
1365
1377
  return_url = request.POST.get("_return_url")
1366
1378
  if return_url is not None and url_has_allowed_host_and_scheme(url=return_url, allowed_hosts=request.get_host()):
@@ -1368,8 +1380,11 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1368
1380
  else:
1369
1381
  return_url = None
1370
1382
 
1371
- # Allow execution only if a worker process is running and the job is runnable.
1372
- if not get_worker_count(queue=task_queue):
1383
+ queue = get_job_queue(job_queue)
1384
+ if queue is None:
1385
+ queue = job_model.default_job_queue
1386
+ # Allow execution only if a worker process is running on a celery queue and the job is runnable.
1387
+ if queue.queue_type == JobQueueTypeChoices.TYPE_CELERY and not get_worker_count(queue=job_queue):
1373
1388
  messages.error(request, "Unable to run or schedule job: Celery worker process not running.")
1374
1389
  elif not job_model.installed or job_class is None:
1375
1390
  messages.error(request, "Unable to run or schedule job: Job is not presently installed.")
@@ -1388,10 +1403,21 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1388
1403
  "One of these two flags must be removed before this job can be scheduled or run.",
1389
1404
  )
1390
1405
  elif job_form is not None and job_form.is_valid() and schedule_form.is_valid():
1391
- task_queue = job_form.cleaned_data.pop("_task_queue", None)
1406
+ job_queue = job_form.cleaned_data.pop("_job_queue", None)
1407
+ jq = None
1408
+ if job_queue is not None:
1409
+ try:
1410
+ jq = JobQueue.objects.get(pk=job_queue)
1411
+ except (ValidationError, JobQueue.DoesNotExist):
1412
+ try:
1413
+ jq = JobQueue.objects.get(name=job_queue)
1414
+ except JobQueue.DoesNotExist:
1415
+ pass
1416
+
1392
1417
  dryrun = job_form.cleaned_data.get("dryrun", False)
1393
1418
  # Run the job. A new JobResult is created.
1394
1419
  profile = job_form.cleaned_data.pop("_profile")
1420
+ ignore_singleton_lock = job_form.cleaned_data.pop("_ignore_singleton_lock", False)
1395
1421
  schedule_type = schedule_form.cleaned_data["_schedule_type"]
1396
1422
 
1397
1423
  if (not dryrun and job_model.approval_required) or schedule_type in JobExecutionType.SCHEDULE_CHOICES:
@@ -1403,8 +1429,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1403
1429
  interval=schedule_type,
1404
1430
  crontab=schedule_form.cleaned_data.get("_recurrence_custom_time"),
1405
1431
  approval_required=job_model.approval_required,
1406
- task_queue=task_queue,
1432
+ task_queue=jq.name if jq else None,
1407
1433
  profile=profile,
1434
+ ignore_singleton_lock=ignore_singleton_lock,
1408
1435
  **job_class.serialize_data(job_form.cleaned_data),
1409
1436
  )
1410
1437
 
@@ -1422,7 +1449,8 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1422
1449
  job_model,
1423
1450
  request.user,
1424
1451
  profile=profile,
1425
- task_queue=task_queue,
1452
+ ignore_singleton_lock=ignore_singleton_lock,
1453
+ task_queue=jq.name if jq else None,
1426
1454
  **job_class.serialize_data(job_kwargs),
1427
1455
  )
1428
1456
 
@@ -1447,7 +1475,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1447
1475
  get_template(job_class.template_name)
1448
1476
  template_name = job_class.template_name
1449
1477
  except TemplateDoesNotExist as err:
1450
- messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1478
+ messages.error(
1479
+ request, f'Unable to render requested custom job template "{job_class.template_name}": {err}'
1480
+ )
1451
1481
 
1452
1482
  return render(
1453
1483
  request,
@@ -1478,23 +1508,6 @@ class JobBulkEditView(generic.BulkEditView):
1478
1508
  form = forms.JobBulkEditForm
1479
1509
  template_name = "extras/job_bulk_edit.html"
1480
1510
 
1481
- def extra_post_save_action(self, obj, form):
1482
- cleaned_data = form.cleaned_data
1483
-
1484
- # Handle text related fields
1485
- for overridable_field in JOB_OVERRIDABLE_FIELDS:
1486
- override_field = overridable_field + "_override"
1487
- clear_override_field = "clear_" + overridable_field + "_override"
1488
- reset_override = cleaned_data.get(clear_override_field, False)
1489
- override_value = cleaned_data.get(overridable_field)
1490
- if reset_override:
1491
- setattr(obj, override_field, False)
1492
- elif not reset_override and override_value not in [None, ""]:
1493
- setattr(obj, override_field, True)
1494
- setattr(obj, overridable_field, override_value)
1495
-
1496
- obj.validated_save()
1497
-
1498
1511
 
1499
1512
  class JobDeleteView(generic.ObjectDeleteView):
1500
1513
  queryset = JobModel.objects.all()
@@ -1534,7 +1547,7 @@ class JobApprovalRequestView(generic.ObjectView):
1534
1547
  if job_class is not None:
1535
1548
  # Render the form with all fields disabled
1536
1549
  initial = instance.kwargs
1537
- initial["_task_queue"] = instance.queue
1550
+ initial["_job_queue"] = instance.queue
1538
1551
  initial["_profile"] = instance.celery_kwargs.get("profile", False)
1539
1552
  job_form = job_class().as_form(initial=initial, approval_view=True)
1540
1553
  else:
@@ -1593,12 +1606,15 @@ class JobApprovalRequestView(generic.ObjectView):
1593
1606
  messages.error(request, "You do not have permission to deny this request.")
1594
1607
  else:
1595
1608
  # Delete the scheduled_job instance
1609
+ publish_event_payload = {"data": serialize_object_v2(scheduled_job)}
1596
1610
  scheduled_job.delete()
1597
1611
  if request.user == scheduled_job.user:
1598
1612
  messages.error(request, f"Approval request for {scheduled_job.name} was revoked")
1599
1613
  else:
1600
1614
  messages.error(request, f"Approval of {scheduled_job.name} was denied")
1601
1615
 
1616
+ publish_event(topic="nautobot.jobs.approval.denied", payload=publish_event_payload)
1617
+
1602
1618
  return redirect("extras:scheduledjob_approval_queue_list")
1603
1619
 
1604
1620
  elif approve or force_approve:
@@ -1620,6 +1636,9 @@ class JobApprovalRequestView(generic.ObjectView):
1620
1636
  scheduled_job.approved_at = timezone.now()
1621
1637
  scheduled_job.save()
1622
1638
 
1639
+ publish_event_payload = {"data": serialize_object_v2(scheduled_job)}
1640
+ publish_event(topic="nautobot.jobs.approval.approved", payload=publish_event_payload)
1641
+
1623
1642
  messages.success(request, f"{scheduled_job.name} was approved and will now begin execution")
1624
1643
 
1625
1644
  return redirect("extras:scheduledjob_approval_queue_list")
@@ -1634,6 +1653,28 @@ class JobApprovalRequestView(generic.ObjectView):
1634
1653
  )
1635
1654
 
1636
1655
 
1656
+ class JobQueueUIViewSet(NautobotUIViewSet):
1657
+ bulk_update_form_class = forms.JobQueueBulkEditForm
1658
+ filterset_form_class = forms.JobQueueFilterForm
1659
+ queryset = JobQueue.objects.all()
1660
+ form_class = forms.JobQueueForm
1661
+ filterset_class = filters.JobQueueFilterSet
1662
+ serializer_class = serializers.JobQueueSerializer
1663
+ table_class = tables.JobQueueTable
1664
+
1665
+ def get_extra_context(self, request, instance):
1666
+ context = super().get_extra_context(request, instance)
1667
+
1668
+ if self.action == "retrieve":
1669
+ jobs = instance.jobs.restrict(request.user, "view")
1670
+ jobs_table = tables.JobTable(jobs, orderable=False)
1671
+ paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
1672
+ RequestConfig(request, paginate).configure(jobs_table)
1673
+ context["jobs_table"] = jobs_table
1674
+
1675
+ return context
1676
+
1677
+
1637
1678
  #
1638
1679
  # Saved Views
1639
1680
  #
@@ -1833,7 +1874,7 @@ class SavedViewUIViewSet(
1833
1874
  if derived_instance and derived_instance.config.get("pagination_count", None):
1834
1875
  pagination_count = derived_instance.config["pagination_count"]
1835
1876
  else:
1836
- pagination_count = get_settings_or_config("PAGINATE_COUNT")
1877
+ pagination_count = get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
1837
1878
  sv.config["pagination_count"] = int(pagination_count)
1838
1879
  sort_order = param_dict.get("sort", [])
1839
1880
  if not sort_order:
@@ -1927,7 +1968,7 @@ class ScheduledJobView(generic.ObjectView):
1927
1968
  return {
1928
1969
  "labels": labels,
1929
1970
  "job_class_found": (job_class is not None),
1930
- "default_time_zone": ZoneInfo(settings.TIME_ZONE),
1971
+ "default_time_zone": get_current_timezone(),
1931
1972
  **super().get_extra_context(request, instance),
1932
1973
  }
1933
1974
 
@@ -1986,6 +2027,9 @@ def get_annotated_jobresult_queryset():
1986
2027
  debug_log_count=count_related(
1987
2028
  JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
1988
2029
  ),
2030
+ success_log_count=count_related(
2031
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
2032
+ ),
1989
2033
  info_log_count=count_related(
1990
2034
  JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
1991
2035
  ),
@@ -2465,6 +2509,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2465
2509
  module_table.columns.hide("role")
2466
2510
  RequestConfig(request, paginate).configure(module_table)
2467
2511
  context["module_table"] = module_table
2512
+ if ContentType.objects.get_for_model(VirtualDeviceContext) in context["content_types"]:
2513
+ vdcs = instance.virtual_device_contexts.restrict(request.user, "view")
2514
+ vdc_table = VirtualDeviceContextTable(vdcs)
2515
+ vdc_table.columns.hide("role")
2516
+ RequestConfig(request, paginate).configure(vdc_table)
2517
+ context["vdc_table"] = vdc_table
2468
2518
  return context
2469
2519
 
2470
2520
 
@@ -2473,38 +2523,56 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2473
2523
  #
2474
2524
 
2475
2525
 
2476
- class SecretListView(generic.ObjectListView):
2477
- queryset = Secret.objects.all()
2478
- filterset = filters.SecretFilterSet
2479
- filterset_form = forms.SecretFilterForm
2480
- table = tables.SecretTable
2481
-
2482
-
2483
- class SecretView(generic.ObjectView):
2526
+ class SecretUIViewSet(
2527
+ ObjectDetailViewMixin,
2528
+ ObjectListViewMixin,
2529
+ ObjectEditViewMixin,
2530
+ ObjectDestroyViewMixin,
2531
+ ObjectBulkCreateViewMixin, # 3.0 TODO: remove, unused
2532
+ ObjectBulkDestroyViewMixin,
2533
+ # no ObjectBulkUpdateViewMixin here yet
2534
+ ObjectChangeLogViewMixin,
2535
+ ObjectNotesViewMixin,
2536
+ ):
2484
2537
  queryset = Secret.objects.all()
2538
+ form_class = forms.SecretForm
2539
+ filterset_class = filters.SecretFilterSet
2540
+ filterset_form_class = forms.SecretFilterForm
2541
+ table_class = tables.SecretTable
2542
+
2543
+ object_detail_content = object_detail.ObjectDetailContent(
2544
+ panels=[
2545
+ object_detail.ObjectFieldsPanel(
2546
+ weight=100, section=SectionChoices.LEFT_HALF, fields="__all__", exclude_fields=["parameters"]
2547
+ ),
2548
+ object_detail.KeyValueTablePanel(
2549
+ weight=200, section=SectionChoices.LEFT_HALF, label="Parameters", context_data_key="parameters"
2550
+ ),
2551
+ object_detail.ObjectsTablePanel(
2552
+ weight=100,
2553
+ section=SectionChoices.RIGHT_HALF,
2554
+ table_title="Groups containing this secret",
2555
+ table_class=tables.SecretsGroupTable,
2556
+ table_attribute="secrets_groups",
2557
+ footer_content_template_path=None,
2558
+ ),
2559
+ ],
2560
+ extra_buttons=[
2561
+ object_detail.Button(
2562
+ weight=100,
2563
+ label="Check Secret",
2564
+ icon="mdi-test-tube",
2565
+ javascript_template_path="extras/secret_check.js",
2566
+ attributes={"onClick": "checkSecret()"},
2567
+ ),
2568
+ ],
2569
+ )
2485
2570
 
2486
2571
  def get_extra_context(self, request, instance):
2487
- # Determine user's preferred output format
2488
- if request.GET.get("format") in ["json", "yaml"]:
2489
- format_ = request.GET.get("format")
2490
- if request.user.is_authenticated:
2491
- request.user.set_config("extras.configcontext.format", format_, commit=True)
2492
- elif request.user.is_authenticated:
2493
- format_ = request.user.get_config("extras.configcontext.format", "json")
2494
- else:
2495
- format_ = "json"
2496
-
2497
- provider = registry["secrets_providers"].get(instance.provider)
2498
-
2499
- groups = instance.secrets_groups.distinct()
2500
- groups_table = tables.SecretsGroupTable(groups, orderable=False)
2501
-
2502
- return {
2503
- "format": format_,
2504
- "provider_name": provider.name if provider else instance.provider,
2505
- "groups_table": groups_table,
2506
- **super().get_extra_context(request, instance),
2507
- }
2572
+ ctx = super().get_extra_context(request, instance)
2573
+ if self.action == "retrieve":
2574
+ ctx["parameters"] = instance.parameters
2575
+ return ctx
2508
2576
 
2509
2577
 
2510
2578
  class SecretProviderParametersFormView(generic.GenericView):
@@ -2523,27 +2591,6 @@ class SecretProviderParametersFormView(generic.GenericView):
2523
2591
  )
2524
2592
 
2525
2593
 
2526
- class SecretEditView(generic.ObjectEditView):
2527
- queryset = Secret.objects.all()
2528
- model_form = forms.SecretForm
2529
- template_name = "extras/secret_edit.html"
2530
-
2531
-
2532
- class SecretDeleteView(generic.ObjectDeleteView):
2533
- queryset = Secret.objects.all()
2534
-
2535
-
2536
- class SecretBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
2537
- queryset = Secret.objects.all()
2538
- table = tables.SecretTable
2539
-
2540
-
2541
- class SecretBulkDeleteView(generic.BulkDeleteView):
2542
- queryset = Secret.objects.all()
2543
- filterset = filters.SecretFilterSet
2544
- table = tables.SecretTable
2545
-
2546
-
2547
2594
  class SecretsGroupListView(generic.ObjectListView):
2548
2595
  queryset = SecretsGroup.objects.all()
2549
2596
  filterset = filters.SecretsGroupFilterSet
@@ -6,12 +6,13 @@ from nautobot.extras.registry import registry
6
6
  from nautobot.extras.tasks import process_webhook
7
7
 
8
8
 
9
- def enqueue_webhooks(object_change, webhook_queryset=None):
9
+ def enqueue_webhooks(object_change, snapshots=None, webhook_queryset=None):
10
10
  """
11
11
  Find Webhook(s) assigned to this instance + action and enqueue them to be processed.
12
12
 
13
13
  Args:
14
14
  object_change (ObjectChange): The change that may trigger Webhooks to be sent.
15
+ snapshots (list): The before/after data snapshots corresponding to the object_change.
15
16
  webhook_queryset (QuerySet): Previously retrieved set of Webhooks to potentially send.
16
17
 
17
18
  Returns:
@@ -34,6 +35,8 @@ def enqueue_webhooks(object_change, webhook_queryset=None):
34
35
  webhook_queryset = Webhook.objects.filter(content_types=content_type, enabled=True, **{action_flag: True})
35
36
 
36
37
  if webhook_queryset: # not .exists() as we *want* to populate the queryset cache
38
+ if snapshots is None:
39
+ snapshots = object_change.get_snapshots()
37
40
  # fall back to object_data if object_data_v2 is not available
38
41
  serialized_data = object_change.object_data_v2
39
42
  if serialized_data is None:
@@ -49,7 +52,7 @@ def enqueue_webhooks(object_change, webhook_queryset=None):
49
52
  str(timezone.now()),
50
53
  object_change.user_name,
51
54
  object_change.request_id,
52
- object_change.get_snapshots(),
55
+ snapshots,
53
56
  ]
54
57
  process_webhook.apply_async(args=args)
55
58
 
@@ -7,11 +7,11 @@ class IPFieldSerializer(serializers.CharField):
7
7
  """Convert internal (IPNetwork) representation to API (string) representation."""
8
8
  return str(value)
9
9
 
10
- def to_internal_value(self, value):
10
+ def to_internal_value(self, data):
11
11
  """Convert API (string) representation to internal (IPNetwork) representation."""
12
12
  try:
13
- return netaddr.IPNetwork(value)
13
+ return netaddr.IPNetwork(data)
14
14
  except netaddr.AddrFormatError:
15
- raise serializers.ValidationError(f"Invalid IP address format: {value}")
15
+ raise serializers.ValidationError(f"Invalid IP address format: {data}")
16
16
  except (TypeError, ValueError) as e:
17
17
  raise serializers.ValidationError(e)