nautobot 2.3.16__py3-none-any.whl → 2.4.0b1__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 (675) hide show
  1. nautobot/__init__.py +15 -0
  2. nautobot/apps/api.py +0 -2
  3. nautobot/apps/config.py +32 -3
  4. nautobot/apps/events.py +19 -0
  5. nautobot/apps/exceptions.py +0 -2
  6. nautobot/apps/ui.py +44 -9
  7. nautobot/apps/utils.py +0 -8
  8. nautobot/apps/views.py +2 -0
  9. nautobot/circuits/navigation.py +0 -57
  10. nautobot/circuits/tables.py +1 -2
  11. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
  12. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
  13. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
  15. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
  16. nautobot/circuits/tests/integration/test_relationships.py +33 -24
  17. nautobot/circuits/tests/test_filters.py +4 -8
  18. nautobot/circuits/views.py +143 -26
  19. nautobot/cloud/factory.py +4 -1
  20. nautobot/cloud/models.py +1 -1
  21. nautobot/cloud/tests/test_filters.py +5 -4
  22. nautobot/core/api/fields.py +5 -5
  23. nautobot/core/api/metadata.py +28 -256
  24. nautobot/core/api/pagination.py +3 -2
  25. nautobot/core/api/renderers.py +3 -0
  26. nautobot/core/api/serializers.py +24 -244
  27. nautobot/core/api/urls.py +3 -4
  28. nautobot/core/api/utils.py +0 -62
  29. nautobot/core/api/views.py +48 -158
  30. nautobot/core/apps/__init__.py +22 -578
  31. nautobot/core/celery/__init__.py +13 -0
  32. nautobot/core/celery/log.py +4 -4
  33. nautobot/core/celery/schedulers.py +48 -3
  34. nautobot/core/cli/__init__.py +8 -0
  35. nautobot/core/constants.py +7 -0
  36. nautobot/core/events/__init__.py +116 -0
  37. nautobot/core/events/base.py +27 -0
  38. nautobot/core/events/exceptions.py +10 -0
  39. nautobot/core/events/redis_broker.py +48 -0
  40. nautobot/core/events/syslog_broker.py +19 -0
  41. nautobot/core/exceptions.py +0 -6
  42. nautobot/core/filters.py +16 -21
  43. nautobot/core/fixtures/user-data.json +59 -0
  44. nautobot/core/forms/fields.py +53 -8
  45. nautobot/core/forms/utils.py +2 -1
  46. nautobot/core/graphql/schema.py +3 -1
  47. nautobot/core/graphql/types.py +1 -1
  48. nautobot/core/jobs/__init__.py +4 -4
  49. nautobot/core/jobs/cleanup.py +13 -49
  50. nautobot/core/jobs/groups.py +1 -1
  51. nautobot/core/management/commands/generate_test_data.py +21 -0
  52. nautobot/core/management/commands/validate_models.py +1 -1
  53. nautobot/core/middleware.py +16 -0
  54. nautobot/core/models/__init__.py +1 -1
  55. nautobot/core/models/fields.py +11 -7
  56. nautobot/core/models/query_functions.py +2 -2
  57. nautobot/core/models/tree_queries.py +3 -6
  58. nautobot/core/settings.py +44 -7
  59. nautobot/core/settings.yaml +86 -8
  60. nautobot/core/tables.py +15 -65
  61. nautobot/core/tasks.py +1 -1
  62. nautobot/core/templates/components/button/default.html +7 -0
  63. nautobot/core/templates/components/button/dropdown.html +20 -0
  64. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  65. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  66. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  67. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  68. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  69. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  70. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  71. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  72. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  73. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  74. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  75. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  76. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  77. nautobot/core/templates/components/panel/panel.html +16 -0
  78. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  79. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  80. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  81. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  82. nautobot/core/templates/generic/object_retrieve.html +28 -17
  83. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  84. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  85. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  86. nautobot/core/templates/inc/footer.html +1 -0
  87. nautobot/core/templates/inc/media.html +0 -3
  88. nautobot/core/templates/inc/nav_menu.html +1 -1
  89. nautobot/core/templates/inc/relationships_panel.html +1 -1
  90. nautobot/core/templates/nautobot_config.py.j2 +3 -3
  91. nautobot/core/templates/panel_table.html +12 -0
  92. nautobot/core/templates/search.html +0 -7
  93. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  94. nautobot/core/templatetags/helpers.py +101 -12
  95. nautobot/core/templatetags/ui_framework.py +40 -0
  96. nautobot/core/testing/api.py +23 -128
  97. nautobot/core/testing/context.py +18 -0
  98. nautobot/core/testing/filters.py +41 -58
  99. nautobot/core/testing/mixins.py +2 -7
  100. nautobot/core/testing/views.py +25 -123
  101. nautobot/core/tests/integration/test_app_home.py +1 -0
  102. nautobot/core/tests/integration/test_app_navbar.py +1 -0
  103. nautobot/core/tests/integration/test_filters.py +2 -0
  104. nautobot/core/tests/integration/test_home.py +1 -0
  105. nautobot/core/tests/integration/test_navbar.py +1 -0
  106. nautobot/core/tests/integration/test_view_authentication.py +1 -2
  107. nautobot/core/tests/nautobot_config.py +198 -0
  108. nautobot/core/tests/runner.py +3 -3
  109. nautobot/core/tests/test_api.py +82 -201
  110. nautobot/core/tests/test_csv.py +3 -25
  111. nautobot/core/tests/test_events.py +214 -0
  112. nautobot/core/tests/test_jinja_filters.py +1 -0
  113. nautobot/core/tests/test_jobs.py +84 -13
  114. nautobot/core/tests/test_navigations.py +7 -241
  115. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  116. nautobot/core/tests/test_ui.py +150 -0
  117. nautobot/core/tests/test_utils.py +0 -25
  118. nautobot/core/tests/test_views.py +123 -31
  119. nautobot/core/ui/__init__.py +0 -0
  120. nautobot/core/ui/base.py +11 -0
  121. nautobot/core/ui/choices.py +44 -0
  122. nautobot/core/ui/homepage.py +167 -0
  123. nautobot/core/ui/nav.py +279 -0
  124. nautobot/core/ui/object_detail.py +1841 -0
  125. nautobot/core/ui/utils.py +36 -0
  126. nautobot/core/urls.py +4 -9
  127. nautobot/core/utils/config.py +30 -3
  128. nautobot/core/utils/lookup.py +20 -13
  129. nautobot/core/views/__init__.py +6 -1
  130. nautobot/core/views/generic.py +47 -52
  131. nautobot/core/views/mixins.py +15 -25
  132. nautobot/core/views/paginator.py +8 -5
  133. nautobot/core/views/renderers.py +3 -3
  134. nautobot/core/views/utils.py +11 -0
  135. nautobot/core/wsgi.py +3 -3
  136. nautobot/dcim/api/serializers.py +80 -179
  137. nautobot/dcim/api/urls.py +5 -0
  138. nautobot/dcim/api/views.py +17 -4
  139. nautobot/dcim/apps.py +1 -0
  140. nautobot/dcim/choices.py +28 -0
  141. nautobot/dcim/factory.py +58 -0
  142. nautobot/dcim/filters/__init__.py +197 -24
  143. nautobot/dcim/forms.py +203 -12
  144. nautobot/dcim/graphql/types.py +2 -2
  145. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  146. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  147. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  148. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  149. nautobot/dcim/models/__init__.py +4 -0
  150. nautobot/dcim/models/device_component_templates.py +2 -2
  151. nautobot/dcim/models/device_components.py +20 -22
  152. nautobot/dcim/models/devices.py +173 -4
  153. nautobot/dcim/models/locations.py +3 -3
  154. nautobot/dcim/models/power.py +5 -6
  155. nautobot/dcim/models/racks.py +6 -6
  156. nautobot/dcim/navigation.py +25 -224
  157. nautobot/dcim/signals.py +44 -0
  158. nautobot/dcim/tables/__init__.py +5 -3
  159. nautobot/dcim/tables/devices.py +96 -2
  160. nautobot/dcim/tables/devicetypes.py +2 -2
  161. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  162. nautobot/dcim/templates/dcim/controller_create.html +1 -0
  163. nautobot/dcim/templates/dcim/controller_retrieve.html +5 -1
  164. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  165. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +66 -0
  166. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +46 -0
  167. nautobot/dcim/templates/dcim/device/base.html +6 -42
  168. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  169. nautobot/dcim/templates/dcim/device.html +3 -1
  170. nautobot/dcim/templates/dcim/interface.html +1 -0
  171. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  172. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  173. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  174. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +76 -0
  175. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +34 -0
  176. nautobot/dcim/tests/test_api.py +172 -61
  177. nautobot/dcim/tests/test_filters.py +171 -109
  178. nautobot/dcim/tests/test_forms.py +2 -51
  179. nautobot/dcim/tests/test_graphql.py +0 -52
  180. nautobot/dcim/tests/test_models.py +126 -4
  181. nautobot/dcim/tests/test_signals.py +1 -0
  182. nautobot/dcim/tests/test_views.py +103 -11
  183. nautobot/dcim/urls.py +72 -27
  184. nautobot/dcim/utils.py +2 -2
  185. nautobot/dcim/views.py +369 -62
  186. nautobot/extras/api/customfields.py +2 -2
  187. nautobot/extras/api/serializers.py +91 -75
  188. nautobot/extras/api/urls.py +4 -0
  189. nautobot/extras/api/views.py +78 -15
  190. nautobot/extras/choices.py +13 -0
  191. nautobot/extras/constants.py +0 -1
  192. nautobot/extras/context_managers.py +23 -6
  193. nautobot/extras/datasources/git.py +4 -1
  194. nautobot/extras/factory.py +27 -0
  195. nautobot/extras/filters/__init__.py +59 -0
  196. nautobot/extras/forms/forms.py +125 -30
  197. nautobot/extras/forms/mixins.py +3 -11
  198. nautobot/extras/graphql/types.py +25 -1
  199. nautobot/extras/group_sync.py +3 -3
  200. nautobot/extras/health_checks.py +2 -1
  201. nautobot/extras/jobs.py +62 -26
  202. nautobot/extras/management/__init__.py +1 -0
  203. nautobot/extras/management/commands/runjob.py +7 -79
  204. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  205. nautobot/extras/management/utils.py +87 -0
  206. nautobot/extras/managers.py +1 -3
  207. nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
  208. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  209. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  210. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  211. nautobot/extras/models/__init__.py +4 -0
  212. nautobot/extras/models/change_logging.py +7 -3
  213. nautobot/extras/models/customfields.py +11 -12
  214. nautobot/extras/models/groups.py +9 -13
  215. nautobot/extras/models/jobs.py +218 -37
  216. nautobot/extras/models/models.py +2 -2
  217. nautobot/extras/models/relationships.py +69 -1
  218. nautobot/extras/models/secrets.py +5 -0
  219. nautobot/extras/navigation.py +20 -262
  220. nautobot/extras/plugins/__init__.py +56 -32
  221. nautobot/extras/plugins/marketplace_manifest.yml +450 -0
  222. nautobot/extras/plugins/urls.py +1 -0
  223. nautobot/extras/plugins/views.py +48 -1
  224. nautobot/extras/signals.py +39 -1
  225. nautobot/extras/tables.py +40 -6
  226. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  227. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  228. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  229. nautobot/extras/templates/extras/job_detail.html +36 -6
  230. nautobot/extras/templates/extras/job_edit.html +5 -2
  231. nautobot/extras/templates/extras/job_list.html +2 -7
  232. nautobot/extras/templates/extras/jobqueue_retrieve.html +44 -0
  233. nautobot/extras/templates/extras/marketplace.html +278 -0
  234. nautobot/extras/templates/extras/plugins_list.html +35 -1
  235. nautobot/extras/templates/extras/plugins_tiles.html +79 -0
  236. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  237. nautobot/extras/templates/extras/secret.html +0 -65
  238. nautobot/extras/templates/extras/secret_check.js +16 -0
  239. nautobot/extras/templates/extras/secret_create.html +114 -0
  240. nautobot/extras/templates/extras/secret_edit.html +1 -114
  241. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  242. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  243. nautobot/extras/templatetags/job_buttons.py +5 -4
  244. nautobot/extras/templatetags/plugins.py +69 -6
  245. nautobot/extras/test_jobs/api_test_job.py +1 -1
  246. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  247. nautobot/extras/test_jobs/dry_run.py +1 -1
  248. nautobot/extras/test_jobs/fail.py +5 -5
  249. nautobot/extras/test_jobs/file_output.py +1 -1
  250. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  251. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  252. nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
  253. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  254. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  255. nautobot/extras/test_jobs/log_redaction.py +1 -1
  256. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  257. nautobot/extras/test_jobs/modify_db.py +1 -1
  258. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  259. nautobot/extras/test_jobs/object_var_required.py +1 -1
  260. nautobot/extras/test_jobs/object_vars.py +1 -1
  261. nautobot/extras/test_jobs/pass.py +3 -3
  262. nautobot/extras/test_jobs/profiling.py +1 -1
  263. nautobot/extras/test_jobs/relative_import.py +3 -3
  264. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  265. nautobot/extras/test_jobs/task_queues.py +1 -1
  266. nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
  267. nautobot/extras/tests/test_api.py +157 -55
  268. nautobot/extras/tests/test_context_managers.py +4 -1
  269. nautobot/extras/tests/test_customfields.py +1 -1
  270. nautobot/extras/tests/test_datasources.py +1 -2
  271. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  272. nautobot/extras/tests/test_filters.py +219 -535
  273. nautobot/extras/tests/test_forms.py +1 -20
  274. nautobot/extras/tests/test_job_variables.py +73 -152
  275. nautobot/extras/tests/test_jobs.py +43 -54
  276. nautobot/extras/tests/test_models.py +71 -16
  277. nautobot/extras/tests/test_relationships.py +5 -2
  278. nautobot/extras/tests/test_utils.py +23 -2
  279. nautobot/extras/tests/test_views.py +183 -43
  280. nautobot/extras/tests/test_webhooks.py +2 -1
  281. nautobot/extras/urls.py +2 -20
  282. nautobot/extras/utils.py +118 -4
  283. nautobot/extras/views.py +203 -92
  284. nautobot/extras/webhooks.py +5 -2
  285. nautobot/ipam/api/fields.py +3 -3
  286. nautobot/ipam/api/serializers.py +36 -137
  287. nautobot/ipam/api/views.py +93 -62
  288. nautobot/ipam/lookups.py +62 -101
  289. nautobot/ipam/models.py +11 -63
  290. nautobot/ipam/navigation.py +0 -90
  291. nautobot/ipam/querysets.py +2 -2
  292. nautobot/ipam/tables.py +6 -20
  293. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  294. nautobot/ipam/templates/ipam/vrf.html +0 -47
  295. nautobot/ipam/tests/test_api.py +8 -419
  296. nautobot/ipam/tests/test_filters.py +39 -119
  297. nautobot/ipam/tests/test_forms.py +47 -51
  298. nautobot/ipam/tests/test_migrations.py +30 -30
  299. nautobot/ipam/tests/test_models.py +0 -41
  300. nautobot/ipam/tests/test_querysets.py +1 -63
  301. nautobot/ipam/urls.py +3 -69
  302. nautobot/ipam/utils/__init__.py +0 -24
  303. nautobot/ipam/views.py +153 -198
  304. nautobot/project-static/css/base.css +38 -3
  305. nautobot/project-static/docs/404.html +421 -19
  306. nautobot/project-static/docs/apps/index.html +421 -19
  307. nautobot/project-static/docs/apps/nautobot-apps.html +421 -19
  308. nautobot/project-static/docs/assets/extra.css +5 -1
  309. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
  310. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
  311. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +421 -19
  312. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +421 -19
  313. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +421 -172
  314. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +421 -19
  315. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +421 -19
  316. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +425 -21
  317. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +421 -19
  318. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +424 -22
  319. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9809 -0
  320. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +424 -63
  321. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +421 -19
  322. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +421 -19
  323. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +457 -20
  324. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +421 -19
  325. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +425 -25
  326. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +457 -19
  327. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +421 -19
  328. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +421 -19
  329. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +425 -215
  330. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +430 -342
  331. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5799 -1054
  332. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +421 -19
  333. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +447 -176
  334. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +460 -21
  335. nautobot/project-static/docs/development/apps/api/configuration-view.html +421 -19
  336. nautobot/project-static/docs/development/apps/api/database-backend-config.html +421 -19
  337. nautobot/project-static/docs/development/apps/api/models/django-admin.html +421 -19
  338. nautobot/project-static/docs/development/apps/api/models/global-search.html +421 -19
  339. nautobot/project-static/docs/development/apps/api/models/graphql.html +421 -19
  340. nautobot/project-static/docs/development/apps/api/models/index.html +421 -19
  341. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +421 -19
  342. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +421 -19
  343. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +421 -19
  344. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +421 -19
  345. nautobot/project-static/docs/development/apps/api/platform-features/index.html +421 -19
  346. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +421 -19
  347. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +421 -19
  348. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +421 -19
  349. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +421 -19
  350. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +424 -41
  351. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +421 -19
  352. nautobot/project-static/docs/development/apps/api/prometheus.html +421 -19
  353. nautobot/project-static/docs/development/apps/api/setup.html +425 -155
  354. nautobot/project-static/docs/development/apps/api/testing.html +421 -19
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +421 -19
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +421 -19
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +421 -19
  358. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +421 -19
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +701 -130
  360. nautobot/project-static/docs/development/apps/api/views/base-template.html +421 -19
  361. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +421 -19
  362. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +421 -19
  363. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +421 -19
  364. nautobot/project-static/docs/development/apps/api/views/index.html +423 -20
  365. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +425 -19
  366. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +451 -19
  367. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +421 -19
  368. nautobot/project-static/docs/development/apps/api/views/notes.html +421 -19
  369. nautobot/project-static/docs/development/apps/api/views/rest-api.html +421 -19
  370. nautobot/project-static/docs/development/apps/api/views/urls.html +421 -19
  371. nautobot/project-static/docs/development/apps/index.html +421 -19
  372. nautobot/project-static/docs/development/apps/migration/code-updates.html +422 -52
  373. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +422 -20
  374. nautobot/project-static/docs/development/apps/migration/from-v1.html +421 -19
  375. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +421 -19
  376. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +421 -19
  377. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +421 -19
  378. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +424 -22
  379. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9219 -0
  380. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9333 -0
  381. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9474 -0
  382. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9517 -0
  383. nautobot/project-static/docs/development/apps/porting-from-netbox.html +424 -22
  384. nautobot/project-static/docs/development/core/application-registry.html +421 -19
  385. nautobot/project-static/docs/development/core/best-practices.html +421 -19
  386. nautobot/project-static/docs/development/core/bootstrap-ui.html +421 -19
  387. nautobot/project-static/docs/development/core/caching.html +421 -19
  388. nautobot/project-static/docs/development/core/controllers.html +423 -19
  389. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +490 -45
  390. nautobot/project-static/docs/development/core/generic-views.html +421 -19
  391. nautobot/project-static/docs/development/core/getting-started.html +566 -179
  392. nautobot/project-static/docs/development/core/homepage.html +432 -30
  393. nautobot/project-static/docs/development/core/index.html +421 -19
  394. nautobot/project-static/docs/development/core/local-k8s.html +9453 -0
  395. nautobot/project-static/docs/development/core/model-checklist.html +424 -22
  396. nautobot/project-static/docs/development/core/model-features.html +421 -19
  397. nautobot/project-static/docs/development/core/natural-keys.html +421 -19
  398. nautobot/project-static/docs/development/core/navigation-menu.html +438 -26
  399. nautobot/project-static/docs/development/core/release-checklist.html +435 -45
  400. nautobot/project-static/docs/development/core/role-internals.html +421 -19
  401. nautobot/project-static/docs/development/core/settings.html +421 -19
  402. nautobot/project-static/docs/development/core/style-guide.html +421 -19
  403. nautobot/project-static/docs/development/core/templates.html +431 -22
  404. nautobot/project-static/docs/development/core/testing.html +421 -19
  405. nautobot/project-static/docs/development/core/ui-component-framework.html +11020 -0
  406. nautobot/project-static/docs/development/core/user-preferences.html +424 -22
  407. nautobot/project-static/docs/development/index.html +421 -19
  408. nautobot/project-static/docs/development/jobs/index.html +546 -160
  409. nautobot/project-static/docs/development/jobs/migration/from-v1.html +421 -19
  410. nautobot/project-static/docs/index.html +421 -19
  411. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  412. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  413. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  414. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  415. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  416. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  417. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  418. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  419. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  420. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  421. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  422. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  423. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  424. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  425. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  426. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  427. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  428. nautobot/project-static/docs/objects.inv +0 -0
  429. nautobot/project-static/docs/overview/application_stack.html +426 -20
  430. nautobot/project-static/docs/overview/design_philosophy.html +421 -19
  431. nautobot/project-static/docs/release-notes/index.html +445 -22
  432. nautobot/project-static/docs/release-notes/version-1.0.html +421 -19
  433. nautobot/project-static/docs/release-notes/version-1.1.html +421 -19
  434. nautobot/project-static/docs/release-notes/version-1.2.html +421 -19
  435. nautobot/project-static/docs/release-notes/version-1.3.html +421 -19
  436. nautobot/project-static/docs/release-notes/version-1.4.html +421 -19
  437. nautobot/project-static/docs/release-notes/version-1.5.html +421 -19
  438. nautobot/project-static/docs/release-notes/version-1.6.html +634 -667
  439. nautobot/project-static/docs/release-notes/version-2.0.html +421 -19
  440. nautobot/project-static/docs/release-notes/version-2.1.html +421 -19
  441. nautobot/project-static/docs/release-notes/version-2.2.html +421 -19
  442. nautobot/project-static/docs/release-notes/version-2.3.html +684 -886
  443. nautobot/project-static/docs/release-notes/version-2.4.html +10007 -0
  444. nautobot/project-static/docs/requirements.txt +2 -2
  445. nautobot/project-static/docs/search/search_index.json +1 -1
  446. nautobot/project-static/docs/sitemap.xml +334 -270
  447. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  448. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +421 -19
  449. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +421 -19
  450. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +423 -21
  451. nautobot/project-static/docs/user-guide/administration/configuration/index.html +433 -32
  452. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +421 -19
  453. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +765 -180
  454. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +421 -19
  455. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +421 -19
  456. nautobot/project-static/docs/user-guide/administration/guides/docker.html +434 -29
  457. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +421 -19
  458. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +421 -19
  459. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +421 -19
  460. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +421 -19
  461. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +421 -19
  462. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +421 -19
  463. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +421 -19
  464. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +421 -19
  465. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +421 -19
  466. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +421 -19
  467. nautobot/project-static/docs/user-guide/administration/installation/index.html +426 -20
  468. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +421 -19
  469. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +421 -19
  470. nautobot/project-static/docs/user-guide/administration/installation/services.html +421 -19
  471. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +421 -19
  472. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +442 -41
  473. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +435 -66
  474. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +435 -66
  475. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +421 -19
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +421 -19
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +421 -19
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +421 -19
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +421 -19
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +421 -19
  481. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +421 -19
  482. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  483. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +421 -19
  484. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +427 -21
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +421 -19
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +421 -19
  487. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +421 -19
  488. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +421 -19
  489. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +421 -19
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +421 -19
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +421 -19
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +421 -19
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +421 -19
  494. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +421 -19
  495. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +421 -19
  496. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +421 -19
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +421 -19
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +421 -19
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +421 -19
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +421 -19
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +421 -19
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +457 -20
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +447 -22
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +421 -19
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +421 -19
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +421 -19
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +421 -19
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +421 -19
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +421 -19
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +421 -19
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +421 -19
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +421 -19
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +421 -19
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +421 -19
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +421 -19
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +421 -19
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +421 -19
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +421 -19
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +421 -19
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +421 -19
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +421 -19
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +421 -19
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +421 -19
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +421 -19
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +421 -19
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +421 -19
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +421 -19
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +421 -19
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +421 -19
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +421 -19
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +421 -19
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +421 -19
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +421 -19
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +421 -19
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +421 -19
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +421 -19
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +421 -19
  538. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9333 -0
  539. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +424 -22
  540. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +421 -19
  541. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +421 -19
  542. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +421 -19
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +421 -19
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +421 -19
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +421 -19
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +421 -19
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +421 -19
  548. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +421 -19
  549. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +421 -19
  550. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +421 -19
  551. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +421 -19
  552. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +421 -19
  553. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +421 -19
  554. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +421 -19
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +421 -19
  556. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +421 -19
  557. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +421 -19
  558. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +421 -19
  559. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +424 -22
  560. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9271 -0
  561. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9175 -0
  562. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9169 -0
  563. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9235 -0
  564. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +421 -19
  565. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +421 -19
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +421 -19
  567. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +421 -19
  568. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +421 -19
  569. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +421 -19
  570. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +421 -19
  571. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +421 -19
  572. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +421 -19
  573. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +421 -19
  574. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +421 -19
  575. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +421 -19
  576. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +421 -19
  577. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +421 -19
  578. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +421 -19
  579. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +421 -19
  580. nautobot/project-static/docs/user-guide/index.html +421 -19
  581. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +424 -22
  582. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +421 -19
  583. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +421 -19
  584. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +421 -19
  585. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +421 -19
  586. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9575 -0
  587. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +424 -22
  588. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +421 -19
  589. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +421 -19
  590. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +421 -19
  591. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +421 -19
  592. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +421 -19
  593. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +426 -20
  594. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +424 -22
  595. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +421 -19
  596. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +421 -19
  597. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9182 -0
  598. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +424 -22
  599. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +421 -19
  600. nautobot/project-static/docs/user-guide/platform-functionality/note.html +421 -19
  601. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +421 -19
  602. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +424 -22
  603. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9250 -0
  604. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +421 -19
  605. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +421 -19
  606. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +424 -22
  607. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +421 -19
  608. nautobot/project-static/docs/user-guide/platform-functionality/role.html +421 -19
  609. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +421 -19
  610. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +421 -19
  611. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +424 -22
  612. nautobot/project-static/docs/user-guide/platform-functionality/status.html +421 -19
  613. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +421 -19
  614. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +489 -56
  615. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +421 -19
  616. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +421 -19
  617. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +421 -19
  618. nautobot/project-static/img/jinja_logo.svg +97 -0
  619. nautobot/project-static/js/forms.js +5 -0
  620. nautobot/project-static/js/nav_menu.js +2 -1
  621. nautobot/tenancy/api/serializers.py +0 -2
  622. nautobot/tenancy/factory.py +1 -1
  623. nautobot/tenancy/navigation.py +0 -29
  624. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  625. nautobot/tenancy/tests/test_filters.py +29 -134
  626. nautobot/tenancy/views.py +32 -23
  627. nautobot/users/admin.py +3 -1
  628. nautobot/users/api/serializers.py +4 -5
  629. nautobot/users/api/views.py +1 -1
  630. nautobot/users/forms.py +19 -0
  631. nautobot/users/templates/users/preferences.html +22 -0
  632. nautobot/users/tests/test_filters.py +1 -19
  633. nautobot/users/tests/test_views.py +57 -0
  634. nautobot/users/utils.py +8 -0
  635. nautobot/users/views.py +48 -11
  636. nautobot/virtualization/api/serializers.py +4 -4
  637. nautobot/virtualization/filters.py +2 -20
  638. nautobot/virtualization/navigation.py +0 -48
  639. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  640. nautobot/virtualization/tests/test_filters.py +57 -183
  641. nautobot/virtualization/views.py +18 -15
  642. nautobot/wireless/__init__.py +0 -0
  643. nautobot/wireless/api/__init__.py +0 -0
  644. nautobot/wireless/api/serializers.py +44 -0
  645. nautobot/wireless/api/urls.py +20 -0
  646. nautobot/wireless/api/views.py +34 -0
  647. nautobot/wireless/apps.py +8 -0
  648. nautobot/wireless/choices.py +345 -0
  649. nautobot/wireless/factory.py +138 -0
  650. nautobot/wireless/filters.py +167 -0
  651. nautobot/wireless/forms.py +283 -0
  652. nautobot/wireless/homepage.py +19 -0
  653. nautobot/wireless/migrations/0001_initial.py +223 -0
  654. nautobot/wireless/migrations/__init__.py +0 -0
  655. nautobot/wireless/models.py +207 -0
  656. nautobot/wireless/navigation.py +105 -0
  657. nautobot/wireless/tables.py +244 -0
  658. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  659. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  660. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  661. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +64 -0
  662. nautobot/wireless/tests/__init__.py +0 -0
  663. nautobot/wireless/tests/test_api.py +247 -0
  664. nautobot/wireless/tests/test_filters.py +54 -0
  665. nautobot/wireless/tests/test_models.py +22 -0
  666. nautobot/wireless/tests/test_views.py +378 -0
  667. nautobot/wireless/urls.py +13 -0
  668. nautobot/wireless/views.py +129 -0
  669. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +11 -14
  670. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +674 -551
  671. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
  672. nautobot/core/utils/navigation.py +0 -54
  673. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
  674. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
  675. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
nautobot/extras/views.py CHANGED
@@ -18,21 +18,21 @@ from django.utils import timezone
18
18
  from django.utils.encoding import iri_to_uri
19
19
  from django.utils.html import format_html
20
20
  from django.utils.http import url_has_allowed_host_and_scheme
21
+ from django.utils.timezone import get_current_timezone
21
22
  from django.views.generic import View
22
23
  from django_tables2 import RequestConfig
23
24
  from jsonschema.validators import Draft7Validator
24
25
  from rest_framework.decorators import action
25
26
  from rest_framework.permissions import IsAuthenticated
26
27
 
27
- try:
28
- from zoneinfo import ZoneInfo
29
- except ImportError: # python 3.8
30
- from backports.zoneinfo import ZoneInfo
31
-
28
+ from nautobot.core.constants import PAGINATE_COUNT_DEFAULT
29
+ from nautobot.core.events import publish_event
32
30
  from nautobot.core.forms import restrict_form_fields
33
31
  from nautobot.core.models.querysets import count_related
34
- from nautobot.core.models.utils import pretty_print_query
32
+ from nautobot.core.models.utils import pretty_print_query, serialize_object_v2
35
33
  from nautobot.core.tables import ButtonsColumn
34
+ from nautobot.core.ui import object_detail
35
+ from nautobot.core.ui.choices import SectionChoices
36
36
  from nautobot.core.utils.config import get_settings_or_config
37
37
  from nautobot.core.utils.lookup import (
38
38
  get_filterset_for_model,
@@ -45,6 +45,7 @@ from nautobot.core.utils.requests import normalize_querydict
45
45
  from nautobot.core.views import generic, viewsets
46
46
  from nautobot.core.views.mixins import (
47
47
  GetReturnURLMixin,
48
+ ObjectBulkCreateViewMixin,
48
49
  ObjectBulkDestroyViewMixin,
49
50
  ObjectBulkUpdateViewMixin,
50
51
  ObjectChangeLogViewMixin,
@@ -52,18 +53,26 @@ from nautobot.core.views.mixins import (
52
53
  ObjectDetailViewMixin,
53
54
  ObjectEditViewMixin,
54
55
  ObjectListViewMixin,
56
+ ObjectNotesViewMixin,
55
57
  ObjectPermissionRequiredMixin,
56
58
  )
57
59
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
58
60
  from nautobot.core.views.utils import prepare_cloned_fields
59
61
  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.dcim.models import Controller, Device, Interface, Module, Rack, VirtualDeviceContext
63
+ from nautobot.dcim.tables import (
64
+ ControllerTable,
65
+ DeviceTable,
66
+ InterfaceTable,
67
+ ModuleTable,
68
+ RackTable,
69
+ VirtualDeviceContextTable,
70
+ )
62
71
  from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
63
72
  from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
64
73
  from nautobot.extras.signals import change_context_state
65
74
  from nautobot.extras.tasks import delete_custom_field_data
66
- from nautobot.extras.utils import get_base_template, get_worker_count
75
+ from nautobot.extras.utils import get_base_template, get_job_queue, get_worker_count
67
76
  from nautobot.ipam.models import IPAddress, Prefix, VLAN
68
77
  from nautobot.ipam.tables import IPAddressTable, PrefixTable, VLANTable
69
78
  from nautobot.virtualization.models import VirtualMachine, VMInterface
@@ -71,7 +80,13 @@ from nautobot.virtualization.tables import VirtualMachineTable, VMInterfaceTable
71
80
 
72
81
  from . import filters, forms, tables
73
82
  from .api import serializers
74
- from .choices import DynamicGroupTypeChoices, JobExecutionType, JobResultStatusChoices, LogLevelChoices
83
+ from .choices import (
84
+ DynamicGroupTypeChoices,
85
+ JobExecutionType,
86
+ JobQueueTypeChoices,
87
+ JobResultStatusChoices,
88
+ LogLevelChoices,
89
+ )
75
90
  from .datasources import (
76
91
  enqueue_git_repository_diff_origin_and_local,
77
92
  enqueue_pull_git_repository_and_refresh_data,
@@ -96,6 +111,7 @@ from .models import (
96
111
  JobButton,
97
112
  JobHook,
98
113
  JobLogEntry,
114
+ JobQueue,
99
115
  JobResult,
100
116
  MetadataType,
101
117
  Note,
@@ -1006,6 +1022,27 @@ class ExternalIntegrationUIViewSet(NautobotUIViewSet):
1006
1022
  serializer_class = serializers.ExternalIntegrationSerializer
1007
1023
  table_class = tables.ExternalIntegrationTable
1008
1024
 
1025
+ object_detail_content = object_detail.ObjectDetailContent(
1026
+ panels=(
1027
+ object_detail.ObjectFieldsPanel(
1028
+ section=SectionChoices.LEFT_HALF,
1029
+ weight=100,
1030
+ # Default ordering with __all__ leaves something to be desired
1031
+ fields=[
1032
+ "name",
1033
+ "remote_url",
1034
+ "http_method",
1035
+ "headers",
1036
+ "verify_ssl",
1037
+ "ca_file_path",
1038
+ "secrets_group",
1039
+ "timeout",
1040
+ "extra_config",
1041
+ ],
1042
+ ),
1043
+ ),
1044
+ )
1045
+
1009
1046
 
1010
1047
  #
1011
1048
  # Git repositories
@@ -1060,10 +1097,10 @@ class GitRepositoryEditView(generic.ObjectEditView):
1060
1097
  obj.user = request.user
1061
1098
  return super().alter_obj(obj, request, url_args, url_kwargs)
1062
1099
 
1063
- def get_return_url(self, request, obj=None, default_return_url=None):
1100
+ def get_return_url(self, request, obj):
1064
1101
  if request.method == "POST":
1065
1102
  return reverse("extras:gitrepository_result", kwargs={"pk": obj.pk})
1066
- return super().get_return_url(request, obj=obj, default_return_url=default_return_url)
1103
+ return super().get_return_url(request, obj)
1067
1104
 
1068
1105
 
1069
1106
  class GitRepositoryDeleteView(generic.ObjectDeleteView):
@@ -1211,27 +1248,27 @@ class ImageAttachmentEditView(generic.ObjectEditView):
1211
1248
  return get_object_or_404(self.queryset, pk=kwargs["pk"])
1212
1249
  return self.queryset.model()
1213
1250
 
1214
- def alter_obj(self, obj, request, url_args, url_kwargs):
1215
- if not obj.present_in_database:
1251
+ def alter_obj(self, imageattachment, request, args, kwargs):
1252
+ if not imageattachment.present_in_database:
1216
1253
  # Assign the parent object based on URL kwargs
1217
- model = url_kwargs.get("model")
1218
- if "object_id" in url_kwargs:
1219
- obj.parent = get_object_or_404(model, pk=url_kwargs["object_id"])
1220
- elif "slug" in url_kwargs:
1221
- obj.parent = get_object_or_404(model, slug=url_kwargs["slug"])
1254
+ model = kwargs.get("model")
1255
+ if "object_id" in kwargs:
1256
+ imageattachment.parent = get_object_or_404(model, pk=kwargs["object_id"])
1257
+ elif "slug" in kwargs:
1258
+ imageattachment.parent = get_object_or_404(model, slug=kwargs["slug"])
1222
1259
  else:
1223
1260
  raise RuntimeError("Neither object_id nor slug were provided?")
1224
- return obj
1261
+ return imageattachment
1225
1262
 
1226
- def get_return_url(self, request, obj=None, default_return_url=None):
1227
- return obj.parent.get_absolute_url()
1263
+ def get_return_url(self, request, imageattachment):
1264
+ return imageattachment.parent.get_absolute_url()
1228
1265
 
1229
1266
 
1230
1267
  class ImageAttachmentDeleteView(generic.ObjectDeleteView):
1231
1268
  queryset = ImageAttachment.objects.all()
1232
1269
 
1233
- def get_return_url(self, request, obj=None, default_return_url=None):
1234
- return obj.parent.get_absolute_url()
1270
+ def get_return_url(self, request, imageattachment):
1271
+ return imageattachment.parent.get_absolute_url()
1235
1272
 
1236
1273
 
1237
1274
  #
@@ -1319,9 +1356,14 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1319
1356
  # for example "?kwargs_from_job_result=<UUID>&integervar=22"
1320
1357
  explicit_initial = initial
1321
1358
  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
1359
+ job_queue = job_result.celery_kwargs.get("queue", None)
1360
+ jq = None
1361
+ if job_queue is not None:
1362
+ try:
1363
+ jq = JobQueue.objects.get(name=job_queue, queue_type=JobQueueTypeChoices.TYPE_CELERY)
1364
+ except JobQueue.DoesNotExist:
1365
+ pass
1366
+ initial["_job_queue"] = jq
1325
1367
  initial["_profile"] = job_result.celery_kwargs.get("nautobot_job_profile", False)
1326
1368
  initial.update(explicit_initial)
1327
1369
  except JobResult.DoesNotExist:
@@ -1337,9 +1379,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1337
1379
  get_template(job_class.template_name)
1338
1380
  template_name = job_class.template_name
1339
1381
  except TemplateDoesNotExist as err:
1340
- messages.error(
1341
- request, f'Unable to render requested custom job template "{job_class.template_name}": {err}'
1342
- )
1382
+ messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1343
1383
  except RuntimeError as err:
1344
1384
  messages.error(request, f"Unable to run or schedule '{job_model}': {err}")
1345
1385
  return redirect("extras:job_list")
@@ -1362,7 +1402,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1362
1402
  job_class = get_job(job_model.class_path, reload=True)
1363
1403
  job_form = job_class.as_form(request.POST, request.FILES) if job_class is not None else None
1364
1404
  schedule_form = forms.JobScheduleForm(request.POST)
1365
- task_queue = request.POST.get("_task_queue")
1405
+ job_queue = request.POST.get("_job_queue")
1366
1406
 
1367
1407
  return_url = request.POST.get("_return_url")
1368
1408
  if return_url is not None and url_has_allowed_host_and_scheme(url=return_url, allowed_hosts=request.get_host()):
@@ -1370,8 +1410,11 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1370
1410
  else:
1371
1411
  return_url = None
1372
1412
 
1373
- # Allow execution only if a worker process is running and the job is runnable.
1374
- if not get_worker_count(queue=task_queue):
1413
+ queue = get_job_queue(job_queue)
1414
+ if queue is None:
1415
+ queue = job_model.default_job_queue
1416
+ # Allow execution only if a worker process is running on a celery queue and the job is runnable.
1417
+ if queue.queue_type == JobQueueTypeChoices.TYPE_CELERY and not get_worker_count(queue=job_queue):
1375
1418
  messages.error(request, "Unable to run or schedule job: Celery worker process not running.")
1376
1419
  elif not job_model.installed or job_class is None:
1377
1420
  messages.error(request, "Unable to run or schedule job: Job is not presently installed.")
@@ -1390,7 +1433,17 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1390
1433
  "One of these two flags must be removed before this job can be scheduled or run.",
1391
1434
  )
1392
1435
  elif job_form is not None and job_form.is_valid() and schedule_form.is_valid():
1393
- task_queue = job_form.cleaned_data.pop("_task_queue", None)
1436
+ job_queue = job_form.cleaned_data.pop("_job_queue", None)
1437
+ jq = None
1438
+ if job_queue is not None:
1439
+ try:
1440
+ jq = JobQueue.objects.get(pk=job_queue)
1441
+ except (ValidationError, JobQueue.DoesNotExist):
1442
+ try:
1443
+ jq = JobQueue.objects.get(name=job_queue)
1444
+ except JobQueue.DoesNotExist:
1445
+ pass
1446
+
1394
1447
  dryrun = job_form.cleaned_data.get("dryrun", False)
1395
1448
  # Run the job. A new JobResult is created.
1396
1449
  profile = job_form.cleaned_data.pop("_profile")
@@ -1405,7 +1458,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1405
1458
  interval=schedule_type,
1406
1459
  crontab=schedule_form.cleaned_data.get("_recurrence_custom_time"),
1407
1460
  approval_required=job_model.approval_required,
1408
- task_queue=task_queue,
1461
+ task_queue=jq.name if jq else None,
1409
1462
  profile=profile,
1410
1463
  **job_class.serialize_data(job_form.cleaned_data),
1411
1464
  )
@@ -1424,7 +1477,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1424
1477
  job_model,
1425
1478
  request.user,
1426
1479
  profile=profile,
1427
- task_queue=task_queue,
1480
+ task_queue=jq.name if jq else None,
1428
1481
  **job_class.serialize_data(job_kwargs),
1429
1482
  )
1430
1483
 
@@ -1449,9 +1502,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1449
1502
  get_template(job_class.template_name)
1450
1503
  template_name = job_class.template_name
1451
1504
  except TemplateDoesNotExist as err:
1452
- messages.error(
1453
- request, f'Unable to render requested custom job template "{job_class.template_name}": {err}'
1454
- )
1505
+ messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1455
1506
 
1456
1507
  return render(
1457
1508
  request,
@@ -1497,6 +1548,35 @@ class JobBulkEditView(generic.BulkEditView):
1497
1548
  setattr(obj, override_field, True)
1498
1549
  setattr(obj, overridable_field, override_value)
1499
1550
 
1551
+ # Handle job queues
1552
+ clear_override_field = "clear_job_queues_override"
1553
+ reset_override = cleaned_data.get(clear_override_field, False)
1554
+ if reset_override:
1555
+ meta_task_queues = obj.job_class.task_queues
1556
+ job_queues = []
1557
+ for queue_name in meta_task_queues:
1558
+ try:
1559
+ job_queues.append(JobQueue.objects.get(name=queue_name))
1560
+ except JobQueue.DoesNotExist:
1561
+ # Do we want to create the Job Queue for the users here if we do not have it in the database?
1562
+ pass
1563
+ obj.job_queues_override = False
1564
+ obj.job_queues.set(job_queues)
1565
+ elif cleaned_data["job_queues"]:
1566
+ obj.job_queues_override = True
1567
+
1568
+ # Handle default job queue
1569
+ clear_override_field = "clear_default_job_queue_override"
1570
+ reset_override = cleaned_data.get(clear_override_field, False)
1571
+ if reset_override:
1572
+ meta_task_queues = obj.job_class.task_queues
1573
+ obj.default_job_queue_override = False
1574
+ obj.default_job_queue, _ = JobQueue.objects.get_or_create(
1575
+ name=meta_task_queues[0] if meta_task_queues else settings.CELERY_TASK_DEFAULT_QUEUE
1576
+ )
1577
+ elif cleaned_data["default_job_queue"]:
1578
+ obj.default_job_queue_override = True
1579
+
1500
1580
  obj.validated_save()
1501
1581
 
1502
1582
 
@@ -1538,7 +1618,7 @@ class JobApprovalRequestView(generic.ObjectView):
1538
1618
  if job_class is not None:
1539
1619
  # Render the form with all fields disabled
1540
1620
  initial = instance.kwargs
1541
- initial["_task_queue"] = instance.queue
1621
+ initial["_job_queue"] = instance.queue
1542
1622
  initial["_profile"] = instance.celery_kwargs.get("profile", False)
1543
1623
  job_form = job_class().as_form(initial=initial, approval_view=True)
1544
1624
  else:
@@ -1597,12 +1677,15 @@ class JobApprovalRequestView(generic.ObjectView):
1597
1677
  messages.error(request, "You do not have permission to deny this request.")
1598
1678
  else:
1599
1679
  # Delete the scheduled_job instance
1680
+ publish_event_payload = {"data": serialize_object_v2(scheduled_job)}
1600
1681
  scheduled_job.delete()
1601
1682
  if request.user == scheduled_job.user:
1602
1683
  messages.error(request, f"Approval request for {scheduled_job.name} was revoked")
1603
1684
  else:
1604
1685
  messages.error(request, f"Approval of {scheduled_job.name} was denied")
1605
1686
 
1687
+ publish_event(topic="nautobot.jobs.approval.denied", payload=publish_event_payload)
1688
+
1606
1689
  return redirect("extras:scheduledjob_approval_queue_list")
1607
1690
 
1608
1691
  elif approve or force_approve:
@@ -1624,6 +1707,9 @@ class JobApprovalRequestView(generic.ObjectView):
1624
1707
  scheduled_job.approved_at = timezone.now()
1625
1708
  scheduled_job.save()
1626
1709
 
1710
+ publish_event_payload = {"data": serialize_object_v2(scheduled_job)}
1711
+ publish_event(topic="nautobot.jobs.approval.approved", payload=publish_event_payload)
1712
+
1627
1713
  messages.success(request, f"{scheduled_job.name} was approved and will now begin execution")
1628
1714
 
1629
1715
  return redirect("extras:scheduledjob_approval_queue_list")
@@ -1638,6 +1724,28 @@ class JobApprovalRequestView(generic.ObjectView):
1638
1724
  )
1639
1725
 
1640
1726
 
1727
+ class JobQueueUIViewSet(NautobotUIViewSet):
1728
+ bulk_update_form_class = forms.JobQueueBulkEditForm
1729
+ filterset_form_class = forms.JobQueueFilterForm
1730
+ queryset = JobQueue.objects.all()
1731
+ form_class = forms.JobQueueForm
1732
+ filterset_class = filters.JobQueueFilterSet
1733
+ serializer_class = serializers.JobQueueSerializer
1734
+ table_class = tables.JobQueueTable
1735
+
1736
+ def get_extra_context(self, request, instance):
1737
+ context = super().get_extra_context(request, instance)
1738
+
1739
+ if self.action == "retrieve":
1740
+ jobs = instance.jobs.restrict(request.user, "view")
1741
+ jobs_table = tables.JobTable(jobs, orderable=False)
1742
+ paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
1743
+ RequestConfig(request, paginate).configure(jobs_table)
1744
+ context["jobs_table"] = jobs_table
1745
+
1746
+ return context
1747
+
1748
+
1641
1749
  #
1642
1750
  # Saved Views
1643
1751
  #
@@ -1837,7 +1945,7 @@ class SavedViewUIViewSet(
1837
1945
  if derived_instance and derived_instance.config.get("pagination_count", None):
1838
1946
  pagination_count = derived_instance.config["pagination_count"]
1839
1947
  else:
1840
- pagination_count = get_settings_or_config("PAGINATE_COUNT")
1948
+ pagination_count = get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
1841
1949
  sv.config["pagination_count"] = int(pagination_count)
1842
1950
  sort_order = param_dict.get("sort", [])
1843
1951
  if not sort_order:
@@ -1931,7 +2039,7 @@ class ScheduledJobView(generic.ObjectView):
1931
2039
  return {
1932
2040
  "labels": labels,
1933
2041
  "job_class_found": (job_class is not None),
1934
- "default_time_zone": ZoneInfo(settings.TIME_ZONE),
2042
+ "default_time_zone": get_current_timezone(),
1935
2043
  **super().get_extra_context(request, instance),
1936
2044
  }
1937
2045
 
@@ -2469,6 +2577,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2469
2577
  module_table.columns.hide("role")
2470
2578
  RequestConfig(request, paginate).configure(module_table)
2471
2579
  context["module_table"] = module_table
2580
+ if ContentType.objects.get_for_model(VirtualDeviceContext) in context["content_types"]:
2581
+ vdcs = instance.virtual_device_contexts.restrict(request.user, "view")
2582
+ vdc_table = VirtualDeviceContextTable(vdcs)
2583
+ vdc_table.columns.hide("role")
2584
+ RequestConfig(request, paginate).configure(vdc_table)
2585
+ context["vdc_table"] = vdc_table
2472
2586
  return context
2473
2587
 
2474
2588
 
@@ -2477,38 +2591,56 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2477
2591
  #
2478
2592
 
2479
2593
 
2480
- class SecretListView(generic.ObjectListView):
2481
- queryset = Secret.objects.all()
2482
- filterset = filters.SecretFilterSet
2483
- filterset_form = forms.SecretFilterForm
2484
- table = tables.SecretTable
2485
-
2486
-
2487
- class SecretView(generic.ObjectView):
2594
+ class SecretUIViewSet(
2595
+ ObjectDetailViewMixin,
2596
+ ObjectListViewMixin,
2597
+ ObjectEditViewMixin,
2598
+ ObjectDestroyViewMixin,
2599
+ ObjectBulkCreateViewMixin, # 3.0 TODO: remove, unused
2600
+ ObjectBulkDestroyViewMixin,
2601
+ # no ObjectBulkUpdateViewMixin here yet
2602
+ ObjectChangeLogViewMixin,
2603
+ ObjectNotesViewMixin,
2604
+ ):
2488
2605
  queryset = Secret.objects.all()
2606
+ form_class = forms.SecretForm
2607
+ filterset_class = filters.SecretFilterSet
2608
+ filterset_form_class = forms.SecretFilterForm
2609
+ table_class = tables.SecretTable
2610
+
2611
+ object_detail_content = object_detail.ObjectDetailContent(
2612
+ panels=[
2613
+ object_detail.ObjectFieldsPanel(
2614
+ weight=100, section=SectionChoices.LEFT_HALF, fields="__all__", exclude_fields=["parameters"]
2615
+ ),
2616
+ object_detail.KeyValueTablePanel(
2617
+ weight=200, section=SectionChoices.LEFT_HALF, label="Parameters", context_data_key="parameters"
2618
+ ),
2619
+ object_detail.ObjectsTablePanel(
2620
+ weight=100,
2621
+ section=SectionChoices.RIGHT_HALF,
2622
+ table_title="Groups containing this secret",
2623
+ table_class=tables.SecretsGroupTable,
2624
+ table_attribute="secrets_groups",
2625
+ footer_content_template_path=None,
2626
+ ),
2627
+ ],
2628
+ extra_buttons=[
2629
+ object_detail.Button(
2630
+ weight=100,
2631
+ label="Check Secret",
2632
+ icon="mdi-test-tube",
2633
+ javascript_template_path="extras/secret_check.js",
2634
+ attributes={"onClick": "checkSecret()"},
2635
+ ),
2636
+ ],
2637
+ )
2489
2638
 
2490
2639
  def get_extra_context(self, request, instance):
2491
- # Determine user's preferred output format
2492
- if request.GET.get("format") in ["json", "yaml"]:
2493
- format_ = request.GET.get("format")
2494
- if request.user.is_authenticated:
2495
- request.user.set_config("extras.configcontext.format", format_, commit=True)
2496
- elif request.user.is_authenticated:
2497
- format_ = request.user.get_config("extras.configcontext.format", "json")
2498
- else:
2499
- format_ = "json"
2500
-
2501
- provider = registry["secrets_providers"].get(instance.provider)
2502
-
2503
- groups = instance.secrets_groups.distinct()
2504
- groups_table = tables.SecretsGroupTable(groups, orderable=False)
2505
-
2506
- return {
2507
- "format": format_,
2508
- "provider_name": provider.name if provider else instance.provider,
2509
- "groups_table": groups_table,
2510
- **super().get_extra_context(request, instance),
2511
- }
2640
+ ctx = super().get_extra_context(request, instance)
2641
+ if self.action == "retrieve":
2642
+ ctx["parameters"] = instance.parameters
2643
+ return ctx
2512
2644
 
2513
2645
 
2514
2646
  class SecretProviderParametersFormView(generic.GenericView):
@@ -2527,27 +2659,6 @@ class SecretProviderParametersFormView(generic.GenericView):
2527
2659
  )
2528
2660
 
2529
2661
 
2530
- class SecretEditView(generic.ObjectEditView):
2531
- queryset = Secret.objects.all()
2532
- model_form = forms.SecretForm
2533
- template_name = "extras/secret_edit.html"
2534
-
2535
-
2536
- class SecretDeleteView(generic.ObjectDeleteView):
2537
- queryset = Secret.objects.all()
2538
-
2539
-
2540
- class SecretBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
2541
- queryset = Secret.objects.all()
2542
- table = tables.SecretTable
2543
-
2544
-
2545
- class SecretBulkDeleteView(generic.BulkDeleteView):
2546
- queryset = Secret.objects.all()
2547
- filterset = filters.SecretFilterSet
2548
- table = tables.SecretTable
2549
-
2550
-
2551
2662
  class SecretsGroupListView(generic.ObjectListView):
2552
2663
  queryset = SecretsGroup.objects.all()
2553
2664
  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, data):
10
+ def to_internal_value(self, value):
11
11
  """Convert API (string) representation to internal (IPNetwork) representation."""
12
12
  try:
13
- return netaddr.IPNetwork(data)
13
+ return netaddr.IPNetwork(value)
14
14
  except netaddr.AddrFormatError:
15
- raise serializers.ValidationError(f"Invalid IP address format: {data}")
15
+ raise serializers.ValidationError(f"Invalid IP address format: {value}")
16
16
  except (TypeError, ValueError) as e:
17
17
  raise serializers.ValidationError(e)