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
@@ -1,9 +1,10 @@
1
1
  from datetime import timedelta
2
+ from http import HTTPStatus
3
+ import json
2
4
  from unittest import mock
3
5
  import urllib.parse
4
6
  import uuid
5
7
 
6
- from django.conf import settings
7
8
  from django.contrib.auth import get_user_model
8
9
  from django.contrib.contenttypes.models import ContentType
9
10
  from django.core.exceptions import ValidationError
@@ -14,10 +15,13 @@ from django.utils import timezone
14
15
  from django.utils.html import escape, format_html
15
16
 
16
17
  from nautobot.circuits.models import Circuit
18
+ from nautobot.core.celery import NautobotKombuJSONEncoder
17
19
  from nautobot.core.choices import ColorChoices
18
20
  from nautobot.core.models.fields import slugify_dashes_to_underscores
21
+ from nautobot.core.models.utils import serialize_object_v2
19
22
  from nautobot.core.templatetags.helpers import bettertitle
20
23
  from nautobot.core.testing import extract_form_failures, extract_page_body, ModelViewTestCase, TestCase, ViewTestCases
24
+ from nautobot.core.testing.context import load_event_broker_override_settings
21
25
  from nautobot.core.testing.utils import disable_warnings, get_deletable_objects, post_data
22
26
  from nautobot.core.utils.permissions import get_permission_for_model
23
27
  from nautobot.dcim.models import (
@@ -35,6 +39,7 @@ from nautobot.extras.choices import (
35
39
  CustomFieldTypeChoices,
36
40
  DynamicGroupTypeChoices,
37
41
  JobExecutionType,
42
+ JobQueueTypeChoices,
38
43
  LogLevelChoices,
39
44
  MetadataTypeDataTypeChoices,
40
45
  ObjectChangeActionChoices,
@@ -59,6 +64,7 @@ from nautobot.extras.models import (
59
64
  Job,
60
65
  JobButton,
61
66
  JobLogEntry,
67
+ JobQueue,
62
68
  JobResult,
63
69
  MetadataType,
64
70
  Note,
@@ -86,6 +92,7 @@ from nautobot.extras.utils import RoleModelsQuery, TaggableClassesQuery
86
92
  from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
87
93
  from nautobot.tenancy.models import Tenant
88
94
  from nautobot.users.models import ObjectPermission
95
+ from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
89
96
 
90
97
  # Use the proper swappable User model
91
98
  User = get_user_model()
@@ -1691,21 +1698,21 @@ class ScheduledJobTestCase(
1691
1698
  user = User.objects.create(username="user1", is_active=True)
1692
1699
  ScheduledJob.objects.create(
1693
1700
  name="test1",
1694
- task="pass.TestPassJob",
1701
+ task="pass.TestPass",
1695
1702
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1696
1703
  user=user,
1697
1704
  start_time=timezone.now(),
1698
1705
  )
1699
1706
  ScheduledJob.objects.create(
1700
1707
  name="test2",
1701
- task="pass.TestPassJob",
1708
+ task="pass.TestPass",
1702
1709
  interval=JobExecutionType.TYPE_DAILY,
1703
1710
  user=user,
1704
1711
  start_time=timezone.now(),
1705
1712
  )
1706
1713
  ScheduledJob.objects.create(
1707
1714
  name="test3",
1708
- task="pass.TestPassJob",
1715
+ task="pass.TestPass",
1709
1716
  interval=JobExecutionType.TYPE_CUSTOM,
1710
1717
  user=user,
1711
1718
  start_time=timezone.now(),
@@ -1719,7 +1726,7 @@ class ScheduledJobTestCase(
1719
1726
  ScheduledJob.objects.create(
1720
1727
  enabled=False,
1721
1728
  name="test4",
1722
- task="pass.TestPassJob",
1729
+ task="pass.TestPass",
1723
1730
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1724
1731
  user=self.user,
1725
1732
  start_time=timezone.now(),
@@ -1736,7 +1743,7 @@ class ScheduledJobTestCase(
1736
1743
  ScheduledJob.objects.create(
1737
1744
  enabled=True,
1738
1745
  name=name,
1739
- task="pass.TestPassJob",
1746
+ task="pass.TestPass",
1740
1747
  interval=JobExecutionType.TYPE_CUSTOM,
1741
1748
  user=self.user,
1742
1749
  start_time=timezone.now(),
@@ -1767,7 +1774,7 @@ class ScheduledJobTestCase(
1767
1774
  ScheduledJob.objects.create(
1768
1775
  enabled=True,
1769
1776
  name="test11",
1770
- task="pass.TestPassJob",
1777
+ task="pass.TestPass",
1771
1778
  interval=JobExecutionType.TYPE_CUSTOM,
1772
1779
  user=self.user,
1773
1780
  start_time=timezone.now(),
@@ -1801,7 +1808,7 @@ class ApprovalQueueTestCase(
1801
1808
  def setUp(self):
1802
1809
  super().setUp()
1803
1810
  self.job_model = Job.objects.get_for_class_path("dry_run.TestDryRun")
1804
- self.job_model_2 = Job.objects.get_for_class_path("fail.TestFailJob")
1811
+ self.job_model_2 = Job.objects.get_for_class_path("fail.TestFail")
1805
1812
 
1806
1813
  ScheduledJob.objects.create(
1807
1814
  name="test1",
@@ -1814,7 +1821,7 @@ class ApprovalQueueTestCase(
1814
1821
  )
1815
1822
  ScheduledJob.objects.create(
1816
1823
  name="test2",
1817
- task="fail.TestFailJob",
1824
+ task="fail.TestFail",
1818
1825
  job_model=self.job_model_2,
1819
1826
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1820
1827
  user=self.user,
@@ -1827,7 +1834,7 @@ class ApprovalQueueTestCase(
1827
1834
 
1828
1835
  ScheduledJob.objects.create(
1829
1836
  name="test4",
1830
- task="pass.TestPassJob",
1837
+ task="pass.TestPass",
1831
1838
  job_model=self.job_model,
1832
1839
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1833
1840
  user=self.user,
@@ -2033,6 +2040,16 @@ class ApprovalQueueTestCase(
2033
2040
  self.assertEqual(1, len(ScheduledJob.objects.filter(pk=instance.pk)), msg=str(user))
2034
2041
 
2035
2042
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2043
+ @load_event_broker_override_settings(
2044
+ EVENT_BROKERS={
2045
+ "SyslogEventBroker": {
2046
+ "CLASS": "nautobot.core.events.SyslogEventBroker",
2047
+ "TOPICS": {
2048
+ "INCLUDE": ["*"],
2049
+ },
2050
+ }
2051
+ }
2052
+ )
2036
2053
  def test_post_deny_different_user_permitted(self):
2037
2054
  """A user with appropriate permissions can deny a job request."""
2038
2055
  user = User.objects.create_user(username="testuser1")
@@ -2053,10 +2070,19 @@ class ApprovalQueueTestCase(
2053
2070
  data = {"_deny": True}
2054
2071
 
2055
2072
  self.client.force_login(user)
2056
- response = self.client.post(self._get_url("view", instance), data)
2073
+ with self.assertLogs("nautobot.events") as cm:
2074
+ response = self.client.post(self._get_url("view", instance), data)
2057
2075
  self.assertRedirects(response, reverse("extras:scheduledjob_approval_queue_list"))
2058
2076
  # Request was deleted
2059
2077
  self.assertEqual(0, len(ScheduledJob.objects.filter(pk=instance.pk)))
2078
+ # Event was published
2079
+ expected_payload = {"data": serialize_object_v2(instance)}
2080
+ self.assertEqual(
2081
+ cm.output,
2082
+ [
2083
+ f"INFO:nautobot.events.nautobot.jobs.approval.denied:{json.dumps(expected_payload, cls=NautobotKombuJSONEncoder, indent=4)}"
2084
+ ],
2085
+ )
2060
2086
 
2061
2087
  # Check object-based permissions are enforced for a different instance
2062
2088
  instance = self._get_queryset().first()
@@ -2114,6 +2140,16 @@ class ApprovalQueueTestCase(
2114
2140
  self.assertIsNone(instance.approved_by_user)
2115
2141
 
2116
2142
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2143
+ @load_event_broker_override_settings(
2144
+ EVENT_BROKERS={
2145
+ "SyslogEventBroker": {
2146
+ "CLASS": "nautobot.core.events.SyslogEventBroker",
2147
+ "TOPICS": {
2148
+ "INCLUDE": ["*"],
2149
+ },
2150
+ }
2151
+ }
2152
+ )
2117
2153
  def test_post_approve_different_user_permitted(self):
2118
2154
  """A user with appropriate permissions can approve a job request."""
2119
2155
  user = User.objects.create_user(username="testuser1")
@@ -2134,11 +2170,21 @@ class ApprovalQueueTestCase(
2134
2170
  data = {"_approve": True}
2135
2171
 
2136
2172
  self.client.force_login(user)
2137
- response = self.client.post(self._get_url("view", instance), data)
2173
+ with self.assertLogs("nautobot.events") as cm:
2174
+ response = self.client.post(self._get_url("view", instance), data)
2175
+
2138
2176
  self.assertRedirects(response, reverse("extras:scheduledjob_approval_queue_list"))
2139
2177
  # Job was scheduled
2140
2178
  instance.refresh_from_db()
2141
2179
  self.assertEqual(instance.approved_by_user, user)
2180
+ # Event was published
2181
+ expected_payload = {"data": serialize_object_v2(instance)}
2182
+ self.assertEqual(
2183
+ cm.output,
2184
+ [
2185
+ f"INFO:nautobot.events.nautobot.jobs.approval.approved:{json.dumps(expected_payload, cls=NautobotKombuJSONEncoder, indent=4)}"
2186
+ ],
2187
+ )
2142
2188
 
2143
2189
  # Check object-based permissions are enforced for a different instance
2144
2190
  instance = self._get_queryset().last()
@@ -2149,6 +2195,26 @@ class ApprovalQueueTestCase(
2149
2195
  self.assertIsNone(instance.approved_by_user)
2150
2196
 
2151
2197
 
2198
+ class JobQueueTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2199
+ model = JobQueue
2200
+
2201
+ @classmethod
2202
+ def setUpTestData(cls):
2203
+ cls.form_data = {
2204
+ "name": "Test Job Queue",
2205
+ "queue_type": JobQueueTypeChoices.TYPE_CELERY,
2206
+ "description": "This is a very detailed description",
2207
+ "tenant": Tenant.objects.first().pk,
2208
+ "tags": [t.pk for t in Tag.objects.get_for_model(JobQueue)],
2209
+ }
2210
+ cls.bulk_edit_data = {
2211
+ "queue_type": JobQueueTypeChoices.TYPE_KUBERNETES,
2212
+ "description": "This is a very detailed new description",
2213
+ "tenant": Tenant.objects.last().pk,
2214
+ # TODO add tests for add_tags/remove_tags fields in TagsBulkEditFormMixin
2215
+ }
2216
+
2217
+
2152
2218
  class JobResultTestCase(
2153
2219
  ViewTestCases.GetObjectViewTestCase,
2154
2220
  ViewTestCases.ListObjectsViewTestCase,
@@ -2159,8 +2225,8 @@ class JobResultTestCase(
2159
2225
 
2160
2226
  @classmethod
2161
2227
  def setUpTestData(cls):
2162
- JobResult.objects.create(name="pass.TestPassJob")
2163
- JobResult.objects.create(name="fail.TestFailJob")
2228
+ JobResult.objects.create(name="pass.TestPass")
2229
+ JobResult.objects.create(name="fail.TestFail")
2164
2230
  JobLogEntry.objects.create(
2165
2231
  log_level=LogLevelChoices.LOG_INFO,
2166
2232
  job_result=JobResult.objects.first(),
@@ -2214,7 +2280,9 @@ class JobTestCase(
2214
2280
  # Job model objects are automatically created during database migrations
2215
2281
 
2216
2282
  # But we do need to make sure the ones we're testing are flagged appropriately
2217
- cls.test_pass = Job.objects.get(job_class_name="TestPassJob")
2283
+ cls.test_pass = Job.objects.get(job_class_name="TestPass")
2284
+ default_job_queue = JobQueue.objects.get(name="default", queue_type=JobQueueTypeChoices.TYPE_CELERY)
2285
+ cls.test_pass.default_job_queue = default_job_queue
2218
2286
  cls.test_pass.enabled = True
2219
2287
  cls.test_pass.save()
2220
2288
 
@@ -2227,6 +2295,7 @@ class JobTestCase(
2227
2295
 
2228
2296
  cls.test_required_args = Job.objects.get(job_class_name="TestRequired")
2229
2297
  cls.test_required_args.enabled = True
2298
+ cls.test_pass.default_job_queue = default_job_queue
2230
2299
  cls.test_required_args.save()
2231
2300
 
2232
2301
  cls.extra_run_urls = (
@@ -2245,12 +2314,16 @@ class JobTestCase(
2245
2314
  enabled=True,
2246
2315
  installed=False,
2247
2316
  )
2317
+ cls.test_not_installed.default_job_queue = default_job_queue
2248
2318
  cls.test_not_installed.validated_save()
2249
2319
 
2250
2320
  cls.data_run_immediately = {
2251
2321
  "_schedule_type": "immediately",
2252
2322
  }
2253
-
2323
+ job_queues = JobQueue.objects.all()[:3]
2324
+ pk_list = [queue.pk for queue in job_queues]
2325
+ pk_list += [default_job_queue.pk]
2326
+ job_queues = JobQueue.objects.filter(pk__in=pk_list)
2254
2327
  cls.form_data = {
2255
2328
  "enabled": True,
2256
2329
  "grouping_override": True,
@@ -2271,8 +2344,9 @@ class JobTestCase(
2271
2344
  "time_limit": 650,
2272
2345
  "has_sensitive_variables": False,
2273
2346
  "has_sensitive_variables_override": True,
2274
- "task_queues": "overridden,priority",
2275
- "task_queues_override": True,
2347
+ "job_queues": [queue.pk for queue in job_queues],
2348
+ "job_queues_override": True,
2349
+ "default_job_queue": default_job_queue.pk,
2276
2350
  }
2277
2351
  # This form is emulating the non-conventional JobBulkEditForm
2278
2352
  cls.bulk_edit_data = {
@@ -2293,8 +2367,10 @@ class JobTestCase(
2293
2367
  "time_limit": "",
2294
2368
  "has_sensitive_variables": False,
2295
2369
  "clear_has_sensitive_variables_override": False,
2296
- "task_queues": "overridden,priority",
2297
- "clear_task_queues_override": False,
2370
+ "job_queues": [queue.pk for queue in job_queues],
2371
+ "clear_job_queues_override": False,
2372
+ "clear_default_job_queue_override": False,
2373
+ "default_job_queue": default_job_queue.pk,
2298
2374
  }
2299
2375
 
2300
2376
  def get_deletable_object(self):
@@ -2400,6 +2476,10 @@ class JobTestCase(
2400
2476
  else:
2401
2477
  self.assertEqual(getattr(instance, overridable_field), old_data[instance.pk][overridable_field])
2402
2478
  self.assertEqual(getattr(instance, override_field), old_data[instance.pk][overridable_field])
2479
+ # Special case for task queues/job queues
2480
+ override_value = self.bulk_edit_data.get("job_queues")
2481
+ self.assertEqual(list(instance.job_queues.values_list("pk", flat=True)), override_value)
2482
+ self.assertEqual(instance.job_queues_override, True)
2403
2483
 
2404
2484
  def validate_object_data_after_bulk_edit(self, pk_list):
2405
2485
  instances = self._get_queryset().filter(pk__in=pk_list)
@@ -2433,7 +2513,7 @@ class JobTestCase(
2433
2513
  self.add_permissions("extras.run_job")
2434
2514
  for run_url in self.run_urls:
2435
2515
  response = self.client.get(run_url)
2436
- self.assertBodyContains(response, "TestPassJob")
2516
+ self.assertBodyContains(response, "TestPass")
2437
2517
 
2438
2518
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2439
2519
  def test_get_run_with_constrained_permission(self):
@@ -2532,12 +2612,12 @@ class JobTestCase(
2532
2612
  self.add_permissions("extras.run_job")
2533
2613
 
2534
2614
  for run_url in (
2535
- reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.TestFailJob"}),
2536
- reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="TestFailJob").pk}),
2615
+ reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.TestFail"}),
2616
+ reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="TestFail").pk}),
2537
2617
  ):
2538
2618
  response = self.client.post(run_url, self.data_run_immediately)
2539
2619
  self.assertBodyContains(response, "Job is not enabled to be run")
2540
- self.assertFalse(JobResult.objects.filter(name="fail.TestFailJob").exists())
2620
+ self.assertFalse(JobResult.objects.filter(name="fail.TestFail").exists())
2541
2621
 
2542
2622
  def test_run_now_missing_args(self):
2543
2623
  self.add_permissions("extras.run_job")
@@ -2565,19 +2645,18 @@ class JobTestCase(
2565
2645
  result = JobResult.objects.latest()
2566
2646
  self.assertRedirects(response, reverse("extras:jobresult", kwargs={"pk": result.pk}))
2567
2647
 
2568
- @mock.patch("nautobot.extras.jobs.task_queues_as_choices")
2569
- def test_rerun_job(self, mock_task_queues_as_choices):
2648
+ def test_rerun_job(self):
2570
2649
  self.add_permissions("extras.run_job")
2571
2650
  self.add_permissions("extras.view_jobresult")
2572
2651
 
2573
- mock_task_queues_as_choices.return_value = [("default", ""), ("queue1", ""), ("uniquequeue", "")]
2652
+ job_queue = JobQueue.objects.create(name="uniquequeue", queue_type=JobQueueTypeChoices.TYPE_CELERY)
2574
2653
  job_celery_kwargs = {
2575
2654
  "nautobot_job_job_model_id": self.test_required_args.id,
2576
2655
  "nautobot_job_profile": True,
2577
2656
  "nautobot_job_user_id": self.user.id,
2578
- "queue": "uniquequeue",
2657
+ "queue": job_queue.name,
2579
2658
  }
2580
-
2659
+ self.test_required_args.job_queues.set([job_queue])
2581
2660
  previous_result = JobResult.objects.create(
2582
2661
  job_model=self.test_required_args,
2583
2662
  user=self.user,
@@ -2588,8 +2667,7 @@ class JobTestCase(
2588
2667
  run_url = reverse("extras:job_run", kwargs={"pk": self.test_required_args.pk})
2589
2668
  response = self.client.get(f"{run_url}?kwargs_from_job_result={previous_result.pk!s}")
2590
2669
  content = extract_page_body(response.content.decode(response.charset))
2591
-
2592
- self.assertInHTML('<option value="uniquequeue" selected>', content)
2670
+ self.assertInHTML(f'<option value="{job_queue.pk}" selected>{job_queue}</option>', content)
2593
2671
  self.assertInHTML(
2594
2672
  '<input type="text" name="var" value="456" class="form-control" required placeholder="None" id="id_var">',
2595
2673
  content,
@@ -2702,12 +2780,12 @@ class JobTestCase(
2702
2780
  self.add_permissions("extras.view_jobresult")
2703
2781
 
2704
2782
  self.test_pass.task_queues = []
2705
- self.test_pass.task_queues_override = True
2783
+ self.test_pass.job_queues_override = True
2706
2784
  self.test_pass.validated_save()
2707
-
2785
+ job_queue = JobQueue.objects.create(name="invalid", queue_type=JobQueueTypeChoices.TYPE_CELERY)
2708
2786
  data = {
2709
2787
  "_schedule_type": "immediately",
2710
- "_task_queue": "invalid",
2788
+ "_job_queue": job_queue.pk,
2711
2789
  }
2712
2790
 
2713
2791
  for run_url in self.run_urls:
@@ -2717,7 +2795,7 @@ class JobTestCase(
2717
2795
  errors = extract_form_failures(response.content.decode(response.charset))
2718
2796
  self.assertEqual(
2719
2797
  errors,
2720
- ["_task_queue: Select a valid choice. invalid is not one of the available choices."],
2798
+ ["_job_queue: Select a valid choice. That choice is not one of the available choices."],
2721
2799
  )
2722
2800
 
2723
2801
  @mock.patch("nautobot.extras.views.get_worker_count", return_value=1)
@@ -2766,6 +2844,59 @@ class JobTestCase(
2766
2844
  response = self.client.get(instance.get_changelog_url())
2767
2845
  self.assertBodyContains(response, f"{instance.name} - Change Log")
2768
2846
 
2847
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2848
+ def test_job_bulk_edit_default_queue_stays_default_after_one_field_update(self):
2849
+ self.add_permissions("extras.change_job")
2850
+ job_class_to_test = "TestPass"
2851
+ job_description = "default job queue test"
2852
+ job_to_test = self.model.objects.get(job_class_name=job_class_to_test)
2853
+ queryset = self.model.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
2854
+ default_job_queue = job_to_test.default_job_queue
2855
+ pk_list = list(queryset.values_list("pk", flat=True))
2856
+
2857
+ data = {
2858
+ "description": job_description,
2859
+ "pk": pk_list,
2860
+ "_apply": True,
2861
+ }
2862
+
2863
+ self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), HTTPStatus.FOUND)
2864
+ instance = self.model.objects.get(job_class_name=job_class_to_test)
2865
+ self.assertEqual(instance.description, job_description)
2866
+ self.assertEqual(instance.default_job_queue, default_job_queue)
2867
+
2868
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2869
+ def test_job_bulk_edit_preserve_job_queues_after_one_field_update(self):
2870
+ self.add_permissions("extras.change_job")
2871
+ job_class_to_test = "TestPass"
2872
+ job_description = "job queues test"
2873
+ job_to_test = self.model.objects.get(job_class_name=job_class_to_test)
2874
+
2875
+ # Simulate 3 job queues set
2876
+ job_queues = JobQueue.objects.all()[:3]
2877
+ job_to_test.job_queues.set(job_queues)
2878
+ job_to_test.job_queues.add(job_to_test.default_job_queue)
2879
+ job_to_test.job_queues_override = True
2880
+ job_to_test.save()
2881
+
2882
+ expected_job_queues = list(job_to_test.job_queues.values_list("pk", flat=True))
2883
+
2884
+ queryset = self.model.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
2885
+ pk_list = list(queryset.values_list("pk", flat=True))
2886
+
2887
+ data = {
2888
+ "description": job_description,
2889
+ "pk": pk_list,
2890
+ "_apply": True,
2891
+ }
2892
+
2893
+ self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), HTTPStatus.FOUND)
2894
+
2895
+ instance = self.model.objects.get(job_class_name=job_class_to_test)
2896
+ self.assertEqual(instance.description, job_description)
2897
+ self.assertEqual(instance.job_queues.count(), 4) # 3 custom one + 1 default
2898
+ self.assertQuerySetEqual(instance.job_queues.all(), JobQueue.objects.filter(pk__in=expected_job_queues))
2899
+
2769
2900
 
2770
2901
  class JobButtonTestCase(
2771
2902
  ViewTestCases.CreateObjectViewTestCase,
@@ -2866,18 +2997,28 @@ class JobButtonRenderingTestCase(TestCase):
2866
2997
 
2867
2998
  def test_task_queue_hidden_input_is_present(self):
2868
2999
  """
2869
- Ensure that the job button respects the job class' task_queues and the job class task_queues[0]/default is passed as a hidden form input.
3000
+ Ensure that the job button respects the job class' task_queues and the job class default job queue is passed as a hidden form input.
2870
3001
  """
2871
- self.job.task_queues_override = True
3002
+ self.job.job_queues_override = True
3003
+ task_queues = ["overriden_queue", "default", "priority"]
3004
+ for queue in task_queues:
3005
+ JobQueue.objects.get_or_create(name=queue, defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY})
2872
3006
  self.job.task_queues = ["overriden_queue", "default", "priority"]
2873
3007
  self.job.save()
2874
3008
  response = self.client.get(self.location_type.get_absolute_url(), follow=True)
2875
- self.assertBodyContains(response, f'<input type="hidden" name="_task_queue" value="{self.job.task_queues[0]}">')
2876
- self.job.task_queues_override = False
3009
+ self.assertEqual(response.status_code, 200)
3010
+ content = extract_page_body(response.content.decode(response.charset))
3011
+ job_queues = self.job.job_queues.all().values_list("name", flat=True)
3012
+ self.assertIn(f'<input type="hidden" name="_job_queue" value="{job_queues[0]}">', content, content)
3013
+
3014
+ self.job.job_queues_override = False
2877
3015
  self.job.save()
3016
+ self.job.job_queues.set([])
2878
3017
  response = self.client.get(self.location_type.get_absolute_url(), follow=True)
2879
- self.assertBodyContains(
2880
- response, f'<input type="hidden" name="_task_queue" value="{settings.CELERY_TASK_DEFAULT_QUEUE}">'
3018
+ self.assertEqual(response.status_code, 200)
3019
+ content = extract_page_body(response.content.decode(response.charset))
3020
+ self.assertIn(
3021
+ f'<input type="hidden" name="_job_queue" value="{self.job.default_job_queue.name}">', content, content
2881
3022
  )
2882
3023
 
2883
3024
  def test_view_object_with_unsafe_text(self):
@@ -3150,6 +3291,7 @@ class RelationshipTestCase(
3150
3291
  IPAddress.objects.all().delete()
3151
3292
  Prefix.objects.update(parent=None)
3152
3293
  Prefix.objects.all().delete()
3294
+ ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.all().delete()
3153
3295
  VLAN.objects.all().delete()
3154
3296
 
3155
3297
  # Parameterized tests (for creating and updating single objects):
@@ -3607,8 +3749,6 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
3607
3749
 
3608
3750
  cls.bulk_edit_data = {
3609
3751
  "color": "000000",
3610
- "description": "I used to be a new role object.",
3611
- "weight": 255,
3612
3752
  }
3613
3753
 
3614
3754
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
@@ -345,7 +345,8 @@ class WebhookTest(APITestCase):
345
345
 
346
346
  all_changes = get_changes_for_model(location)
347
347
  self.assertEqual(all_changes.count(), 1)
348
- mock_enqueue_webhooks.assert_called_once_with(all_changes.first(), webhook_queryset=None)
348
+ change = all_changes.first()
349
+ mock_enqueue_webhooks.assert_called_once_with(change, snapshots=change.get_snapshots(), webhook_queryset=None)
349
350
 
350
351
  def test_all_webhook_supported_models(self):
351
352
  """
nautobot/extras/urls.py CHANGED
@@ -16,7 +16,6 @@ from nautobot.extras.models import (
16
16
  JobHook,
17
17
  Note,
18
18
  Relationship,
19
- Secret,
20
19
  SecretsGroup,
21
20
  Status,
22
21
  Tag,
@@ -30,10 +29,12 @@ router.register("contacts", views.ContactUIViewSet)
30
29
  router.register("contact-associations", views.ContactAssociationUIViewSet)
31
30
  router.register("external-integrations", views.ExternalIntegrationUIViewSet)
32
31
  router.register("job-buttons", views.JobButtonUIViewSet)
32
+ router.register("job-queues", views.JobQueueUIViewSet)
33
33
  router.register("metadata-types", views.MetadataTypeUIViewSet)
34
34
  router.register("object-metadata", views.ObjectMetadataUIViewSet)
35
35
  router.register("roles", views.RoleUIViewSet)
36
36
  router.register("saved-views", views.SavedViewUIViewSet)
37
+ router.register("secrets", views.SecretUIViewSet)
37
38
  router.register("static-group-associations", views.StaticGroupAssociationUIViewSet)
38
39
  router.register("teams", views.TeamUIViewSet)
39
40
 
@@ -565,30 +566,11 @@ urlpatterns = [
565
566
  name="relationshipassociation_delete",
566
567
  ),
567
568
  # Secrets
568
- path("secrets/", views.SecretListView.as_view(), name="secret_list"),
569
- path("secrets/add/", views.SecretEditView.as_view(), name="secret_add"),
570
- path("secrets/delete/", views.SecretBulkDeleteView.as_view(), name="secret_bulk_delete"),
571
- path("secrets/import/", views.SecretBulkImportView.as_view(), name="secret_import"), # 3.0 TODO: remove, unused
572
569
  path(
573
570
  "secrets/provider/<str:provider_slug>/form/",
574
571
  views.SecretProviderParametersFormView.as_view(),
575
572
  name="secret_provider_parameters_form",
576
573
  ),
577
- path("secrets/<uuid:pk>/", views.SecretView.as_view(), name="secret"),
578
- path("secrets/<uuid:pk>/edit/", views.SecretEditView.as_view(), name="secret_edit"),
579
- path("secrets/<uuid:pk>/delete/", views.SecretDeleteView.as_view(), name="secret_delete"),
580
- path(
581
- "secrets/<uuid:pk>/changelog/",
582
- views.ObjectChangeLogView.as_view(),
583
- name="secret_changelog",
584
- kwargs={"model": Secret},
585
- ),
586
- path(
587
- "secrets/<uuid:pk>/notes/",
588
- views.ObjectNotesView.as_view(),
589
- name="secret_notes",
590
- kwargs={"model": Secret},
591
- ),
592
574
  path("secrets-groups/", views.SecretsGroupListView.as_view(), name="secretsgroup_list"),
593
575
  path("secrets-groups/add/", views.SecretsGroupEditView.as_view(), name="secretsgroup_add"),
594
576
  path("secrets-groups/delete/", views.SecretsGroupBulkDeleteView.as_view(), name="secretsgroup_bulk_delete"),