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
@@ -4,13 +4,12 @@ import warnings
4
4
  from django.contrib.auth import get_user_model
5
5
  from django.contrib.contenttypes.models import ContentType
6
6
  from django.db.models import Q
7
- from django.forms import ChoiceField, MultipleChoiceField
8
7
  from django.test import override_settings, TestCase
9
8
 
10
9
  from nautobot.dcim.forms import DeviceForm, LocationBulkEditForm, LocationForm
11
10
  import nautobot.dcim.models as dcim_models
12
11
  from nautobot.dcim.models import Device, Location, LocationType
13
- from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoices
12
+ from nautobot.extras.choices import RelationshipTypeChoices
14
13
  from nautobot.extras.forms import (
15
14
  ConfigContextFilterForm,
16
15
  ConfigContextForm,
@@ -26,7 +25,6 @@ from nautobot.extras.forms import (
26
25
  WebhookForm,
27
26
  )
28
27
  from nautobot.extras.models import (
29
- CustomField,
30
28
  Job,
31
29
  JobButton,
32
30
  JobHook,
@@ -1219,20 +1217,3 @@ class CustomFieldModelFormMixinTestCase(TestCase):
1219
1217
 
1220
1218
  custom_field_form = TestForm()
1221
1219
  self.assertIn("_custom_field_data", custom_field_form.fields)
1222
-
1223
-
1224
- class CustomFieldTestCase(TestCase):
1225
- def test_to_form_field_type_select(self):
1226
- """Verify that `to_form_field` and `to_filter_form_field` return the correct field types for a select-type CustomField."""
1227
- custom_field = CustomField.objects.create(
1228
- type=CustomFieldTypeChoices.TYPE_SELECT,
1229
- label="Custom Field Select",
1230
- )
1231
- form = custom_field.to_filter_form_field()
1232
- self.assertIsInstance(form, MultipleChoiceField)
1233
-
1234
- form = custom_field.to_form_field(for_filter_form=True)
1235
- self.assertIsInstance(form, MultipleChoiceField)
1236
-
1237
- form = custom_field.to_form_field(for_filter_form=False)
1238
- self.assertIsInstance(form, ChoiceField)
@@ -1,6 +1,6 @@
1
+ from django.core.exceptions import ValidationError
1
2
  from django.core.files.uploadedfile import SimpleUploadedFile
2
3
  from django.test import TestCase
3
- from netaddr import IPAddress, IPNetwork
4
4
 
5
5
  from nautobot.dcim.models import Device
6
6
  from nautobot.extras.jobs import (
@@ -11,7 +11,6 @@ from nautobot.extras.jobs import (
11
11
  IPAddressVar,
12
12
  IPAddressWithMaskVar,
13
13
  IPNetworkVar,
14
- Job,
15
14
  JSONVar,
16
15
  MultiChoiceVar,
17
16
  MultiObjectVar,
@@ -26,238 +25,160 @@ CHOICES = (("ff0000", "Red"), ("00ff00", "Green"), ("0000ff", "Blue"))
26
25
 
27
26
  class JobVariablesTest(TestCase):
28
27
  def test_stringvar(self):
29
- class StringVarJob(Job):
30
- var1 = StringVar(min_length=3, max_length=3, regex=r"[a-z]+")
28
+ field = StringVar(min_length=3, max_length=3, regex=r"[a-z]+").as_field()
31
29
 
32
30
  # Validate min_length enforcement
33
- data = {"var1": "xx"}
34
- form = StringVarJob().as_form(data)
35
- self.assertFalse(form.is_valid())
36
- self.assertIn("var1", form.errors)
31
+ with self.assertRaises(ValidationError) as cm:
32
+ field.clean("xx")
33
+ self.assertIn("Ensure this value has at least 3 characters (it has 2).", str(cm.exception))
37
34
 
38
35
  # Validate max_length enforcement
39
- data = {"var1": "xxxx"}
40
- form = StringVarJob().as_form(data)
41
- self.assertFalse(form.is_valid())
42
- self.assertIn("var1", form.errors)
36
+ with self.assertRaises(ValidationError) as cm:
37
+ field.clean("xxxx")
38
+ self.assertIn("Ensure this value has at most 3 characters (it has 4).", str(cm.exception))
43
39
 
44
40
  # Validate regex enforcement
45
- data = {"var1": "ABC"}
46
- form = StringVarJob().as_form(data)
47
- self.assertFalse(form.is_valid())
48
- self.assertIn("var1", form.errors)
41
+ with self.assertRaises(ValidationError) as cm:
42
+ field.clean("ABC")
43
+ self.assertIn("Invalid value. Must match regex: [a-z]+'", str(cm.exception))
49
44
 
50
45
  # Validate valid data
51
- data = {"var1": "abc"}
52
- form = StringVarJob().as_form(data)
53
- self.assertTrue(form.is_valid())
54
- self.assertEqual(form.cleaned_data["var1"], data["var1"])
46
+ field.clean("abc")
55
47
 
56
48
  def test_textvar(self):
57
- class TextVarJob(Job):
58
- var1 = TextVar()
49
+ field = TextVar().as_field()
59
50
 
60
51
  # Validate valid data
61
- data = {"var1": "This is a test string"}
62
- form = TextVarJob().as_form(data)
63
- self.assertTrue(form.is_valid())
64
- self.assertEqual(form.cleaned_data["var1"], data["var1"])
52
+ field.clean("This is a test string")
65
53
 
66
54
  def test_integervar(self):
67
- class IntegerVarJob(Job):
68
- var1 = IntegerVar(min_value=5, max_value=10)
55
+ field = IntegerVar(min_value=5, max_value=10).as_field()
69
56
 
70
57
  # Validate min_value enforcement
71
- data = {"var1": 4}
72
- form = IntegerVarJob().as_form(data)
73
- self.assertFalse(form.is_valid())
74
- self.assertIn("var1", form.errors)
58
+ with self.assertRaises(ValidationError) as cm:
59
+ field.clean(4)
60
+ self.assertIn("Ensure this value is greater than or equal to 5.", str(cm.exception))
75
61
 
76
62
  # Validate max_value enforcement
77
- data = {"var1": 11}
78
- form = IntegerVarJob().as_form(data)
79
- self.assertFalse(form.is_valid())
80
- self.assertIn("var1", form.errors)
63
+ with self.assertRaises(ValidationError) as cm:
64
+ field.clean(11)
65
+ self.assertIn("Ensure this value is less than or equal to 10.", str(cm.exception))
81
66
 
82
67
  # Validate valid data
83
- data = {"var1": 7}
84
- form = IntegerVarJob().as_form(data)
85
- self.assertTrue(form.is_valid())
86
- self.assertEqual(form.cleaned_data["var1"], data["var1"])
68
+ field.clean(7)
87
69
 
88
70
  def test_booleanvar(self):
89
- class BooleanVarJob(Job):
90
- var1 = BooleanVar()
71
+ field = BooleanVar().as_field()
91
72
 
92
73
  # Validate True
93
- data = {"var1": True}
94
- form = BooleanVarJob().as_form(data)
95
- self.assertTrue(form.is_valid())
96
- self.assertEqual(form.cleaned_data["var1"], True)
74
+ field.clean(True)
97
75
 
98
76
  # Validate False
99
- data = {"var1": False}
100
- form = BooleanVarJob().as_form(data)
101
- self.assertTrue(form.is_valid())
102
- self.assertEqual(form.cleaned_data["var1"], False)
77
+ field.clean(False)
103
78
 
104
79
  def test_choicevar(self):
105
- class ChoiceVarJob(Job):
106
- var1 = ChoiceVar(choices=CHOICES)
80
+ field = ChoiceVar(choices=CHOICES).as_field()
107
81
 
108
82
  # Validate valid choice
109
- data = {"var1": "ff0000"}
110
- form = ChoiceVarJob().as_form(data)
111
- self.assertTrue(form.is_valid())
112
- self.assertEqual(form.cleaned_data["var1"], "ff0000")
83
+ field.clean("ff0000")
113
84
 
114
85
  # Validate invalid choice
115
- data = {"var1": "taupe"}
116
- form = ChoiceVarJob().as_form(data)
117
- self.assertFalse(form.is_valid())
118
- self.assertIn("var1", form.errors)
86
+ with self.assertRaises(ValidationError) as cm:
87
+ field.clean("taupe")
88
+ self.assertIn("Select a valid choice. taupe is not one of the available choices.", str(cm.exception))
119
89
 
120
90
  def test_multichoicevar(self):
121
- class MultiChoiceVarJob(Job):
122
- var1 = MultiChoiceVar(choices=CHOICES)
91
+ field = MultiChoiceVar(choices=CHOICES).as_field()
123
92
 
124
- # Validate single choice
125
- data = {"var1": ["ff0000"]}
126
- form = MultiChoiceVarJob().as_form(data)
127
- self.assertTrue(form.is_valid())
128
- self.assertEqual(form.cleaned_data["var1"], ["ff0000"])
93
+ # Validate valid choice
94
+ field.clean(["ff0000"])
129
95
 
130
96
  # Validate multiple choices
131
- data = {"var1": ("ff0000", "00ff00")}
132
- form = MultiChoiceVarJob().as_form(data)
133
- self.assertTrue(form.is_valid())
134
- self.assertEqual(form.cleaned_data["var1"], ["ff0000", "00ff00"])
97
+ field.clean(("ff0000", "00ff00"))
135
98
 
136
99
  # Validate invalid choice
137
- data = {"var1": "taupe"}
138
- form = MultiChoiceVarJob().as_form(data)
139
- self.assertFalse(form.is_valid())
140
- self.assertIn("var1", form.errors)
100
+ with self.assertRaises(ValidationError) as cm:
101
+ field.clean(["taupe"])
102
+ self.assertIn("Select a valid choice. taupe is not one of the available choices.", str(cm.exception))
141
103
 
142
104
  def test_objectvar(self):
143
- class ObjectVarJob(Job):
144
- var1 = ObjectVar(model=Role)
105
+ field = ObjectVar(model=Role).as_field()
145
106
 
146
107
  # Validate valid data
147
- data = {"var1": Role.objects.get_for_model(Device).first().pk}
148
- form = ObjectVarJob().as_form(data)
149
- self.assertTrue(form.is_valid())
150
- self.assertEqual(form.cleaned_data["var1"].pk, data["var1"])
108
+ field.clean(Role.objects.get_for_model(Device).first().pk)
151
109
 
152
110
  def test_multiobjectvar(self):
153
- class MultiObjectVarJob(Job):
154
- var1 = MultiObjectVar(model=Role)
111
+ field = MultiObjectVar(model=Role).as_field()
155
112
 
156
113
  # Validate valid data
157
- data = {"var1": [role.pk for role in Role.objects.all()[:3]]}
158
- form = MultiObjectVarJob().as_form(data)
159
- self.assertTrue(form.is_valid())
160
- self.assertEqual(form.cleaned_data["var1"][0].pk, data["var1"][0])
161
- self.assertEqual(form.cleaned_data["var1"][1].pk, data["var1"][1])
162
- self.assertEqual(form.cleaned_data["var1"][2].pk, data["var1"][2])
114
+ field.clean([role.pk for role in Role.objects.all()[:3]])
163
115
 
164
116
  def test_filevar(self):
165
- class FileVarJob(Job):
166
- var1 = FileVar()
117
+ field = FileVar().as_field()
167
118
 
168
119
  # Test file
169
120
  testfile = SimpleUploadedFile(name="test_file.txt", content=b"This is an test file for testing")
170
-
171
121
  # Validate valid data
172
- file_data = {"var1": testfile}
173
- form = FileVarJob().as_form(data=None, files=file_data)
174
- self.assertTrue(form.is_valid())
175
- self.assertEqual(form.cleaned_data["var1"], testfile)
122
+ field.clean(testfile)
176
123
 
177
124
  def test_ipaddressvar(self):
178
- class IPAddressVarJob(Job):
179
- var1 = IPAddressVar()
125
+ field = IPAddressVar().as_field()
180
126
 
181
127
  # Validate IP network enforcement
182
- data = {"var1": "1.2.3"}
183
- form = IPAddressVarJob().as_form(data)
184
- self.assertFalse(form.is_valid())
185
- self.assertIn("var1", form.errors)
128
+ with self.assertRaises(ValidationError) as cm:
129
+ field.clean("1.2.3")
130
+ self.assertIn("Invalid IPv4/IPv6 address format: 1.2.3", str(cm.exception))
186
131
 
187
132
  # Validate IP mask exclusion
188
- data = {"var1": "192.0.2.0/24"}
189
- form = IPAddressVarJob().as_form(data)
190
- self.assertFalse(form.is_valid())
191
- self.assertIn("var1", form.errors)
133
+ with self.assertRaises(ValidationError) as cm:
134
+ field.clean("192.0.2.0/24")
135
+ self.assertIn("Invalid IPv4/IPv6 address format: 192.0.2.0/24", str(cm.exception))
192
136
 
193
137
  # Validate valid data
194
- data = {"var1": "192.0.2.1"}
195
- form = IPAddressVarJob().as_form(data)
196
- self.assertTrue(form.is_valid())
197
- self.assertEqual(form.cleaned_data["var1"], IPAddress(data["var1"]))
138
+ field.clean("192.0.2.1")
198
139
 
199
140
  def test_ipaddresswithmaskvar(self):
200
- class IPAddressWithMaskVarJob(Job):
201
- var1 = IPAddressWithMaskVar()
141
+ field = IPAddressWithMaskVar().as_field()
202
142
 
203
143
  # Validate IP network enforcement
204
- data = {"var1": "1.2.3"}
205
- form = IPAddressWithMaskVarJob().as_form(data)
206
- self.assertFalse(form.is_valid())
207
- self.assertIn("var1", form.errors)
144
+ with self.assertRaises(ValidationError) as cm:
145
+ field.clean("1.2.3")
146
+ self.assertIn("CIDR mask (e.g. /24) is required.", str(cm.exception))
208
147
 
209
148
  # Validate IP mask requirement
210
- data = {"var1": "192.0.2.0"}
211
- form = IPAddressWithMaskVarJob().as_form(data)
212
- self.assertFalse(form.is_valid())
213
- self.assertIn("var1", form.errors)
149
+ with self.assertRaises(ValidationError) as cm:
150
+ field.clean("192.0.2.0")
151
+ self.assertIn("CIDR mask (e.g. /24) is required.", str(cm.exception))
214
152
 
215
153
  # Validate valid data
216
- data = {"var1": "192.0.2.0/24"}
217
- form = IPAddressWithMaskVarJob().as_form(data)
218
- self.assertTrue(form.is_valid())
219
- self.assertEqual(form.cleaned_data["var1"], IPNetwork(data["var1"]))
154
+ field.clean("192.0.2.0/24")
220
155
 
221
156
  def test_ipnetworkvar(self):
222
- class IPNetworkVarJob(Job):
223
- var1 = IPNetworkVar()
157
+ field = IPNetworkVar().as_field()
224
158
 
225
159
  # Validate IP network enforcement
226
- data = {"var1": "1.2.3"}
227
- form = IPNetworkVarJob().as_form(data)
228
- self.assertFalse(form.is_valid())
229
- self.assertIn("var1", form.errors)
160
+ with self.assertRaises(ValidationError) as cm:
161
+ field.clean("1.2.3")
162
+ self.assertIn("CIDR mask (e.g. /24) is required.", str(cm.exception))
230
163
 
231
164
  # Validate host IP check
232
- data = {"var1": "192.0.2.1/24"}
233
- form = IPNetworkVarJob().as_form(data)
234
- self.assertFalse(form.is_valid())
235
- self.assertIn("var1", form.errors)
165
+ with self.assertRaises(ValidationError) as cm:
166
+ field.clean("192.0.2.1/24")
167
+ self.assertIn("192.0.2.1/24 is not a valid prefix. Did you mean 192.0.2.0/24?", str(cm.exception))
236
168
 
237
169
  # Validate valid data
238
- data = {"var1": "192.0.2.0/24"}
239
- form = IPNetworkVarJob().as_form(data)
240
- self.assertTrue(form.is_valid())
241
- self.assertEqual(form.cleaned_data["var1"], IPNetwork(data["var1"]))
170
+ field.clean("192.0.2.0/24")
242
171
 
243
172
  def test_jsonvar(self):
244
- class JSONVarJob(Job):
245
- var1 = JSONVar()
173
+ field = JSONVar().as_field()
246
174
 
247
175
  # Valid JSON value as dictionary
248
- data = {"var1": {"key1": "value1"}}
249
- form = JSONVarJob().as_form(data)
250
- self.assertTrue(form.is_valid())
251
- self.assertEqual(form.cleaned_data["var1"], {"key1": "value1"})
176
+ field.clean({"key1": "value1"})
252
177
 
253
178
  # Valid JSON value as jsonified dictionary
254
- data = {"var1": '{"key1": "value1"}'}
255
- form = JSONVarJob().as_form(data)
256
- self.assertTrue(form.is_valid())
257
- self.assertEqual(form.cleaned_data["var1"], {"key1": "value1"})
179
+ field.clean('{"key1": "value1"}')
258
180
 
259
181
  # Invalid JSON value
260
- data = {"var1": '{"key1": True}'}
261
- form = JSONVarJob().as_form(data)
262
- self.assertFalse(form.is_valid())
263
- self.assertIn("var1", form.errors)
182
+ with self.assertRaises(ValidationError) as cm:
183
+ field.clean('{"key1": True}')
184
+ self.assertIn("Enter a valid JSON.", str(cm.exception))
@@ -12,6 +12,7 @@ from constance.test import override_config
12
12
  from django.conf import settings
13
13
  from django.contrib.contenttypes.models import ContentType
14
14
  from django.core.cache import cache
15
+ from django.core.exceptions import ValidationError
15
16
  from django.core.files.uploadedfile import SimpleUploadedFile
16
17
  from django.core.management import call_command
17
18
  from django.core.management.base import CommandError
@@ -30,6 +31,7 @@ from nautobot.dcim.models import Device, Location, LocationType
30
31
  from nautobot.extras import models
31
32
  from nautobot.extras.choices import (
32
33
  JobExecutionType,
34
+ JobQueueTypeChoices,
33
35
  JobResultStatusChoices,
34
36
  LogLevelChoices,
35
37
  ObjectChangeEventContextChoices,
@@ -71,7 +73,7 @@ class JobTest(TestCase):
71
73
  name = "TestFieldOrder"
72
74
  job_class = get_job(f"{module}.{name}")
73
75
  form = job_class().as_form()
74
- self.assertSequenceEqual(list(form.fields.keys()), ["var1", "var2", "var23", "_task_queue", "_profile"])
76
+ self.assertSequenceEqual(list(form.fields.keys()), ["var1", "var2", "var23", "_job_queue", "_profile"])
75
77
 
76
78
  def test_no_field_order(self):
77
79
  """
@@ -81,7 +83,7 @@ class JobTest(TestCase):
81
83
  name = "TestNoFieldOrder"
82
84
  job_class = get_job(f"{module}.{name}")
83
85
  form = job_class().as_form()
84
- self.assertSequenceEqual(list(form.fields.keys()), ["var23", "var2", "_task_queue", "_profile"])
86
+ self.assertSequenceEqual(list(form.fields.keys()), ["var23", "var2", "_job_queue", "_profile"])
85
87
 
86
88
  def test_no_field_order_inherited_variable(self):
87
89
  """
@@ -93,7 +95,7 @@ class JobTest(TestCase):
93
95
  form = job_class().as_form()
94
96
  self.assertSequenceEqual(
95
97
  list(form.fields.keys()),
96
- ["testvar1", "b_testvar2", "a_testvar3", "_task_queue", "_profile"],
98
+ ["testvar1", "b_testvar2", "a_testvar3", "_job_queue", "_profile"],
97
99
  )
98
100
 
99
101
  def test_dryrun_default(self):
@@ -115,47 +117,36 @@ class JobTest(TestCase):
115
117
  form = job_class().as_form()
116
118
  self.assertEqual(form.fields["dryrun"].initial, job_model.dryrun_default)
117
119
 
118
- @mock.patch("nautobot.extras.utils.get_celery_queues")
119
- def test_job_class_task_queues(self, mock_get_celery_queues):
120
- """
121
- Test job form with custom task queues defined on the job class
122
- """
123
- module = "task_queues"
124
- name = "TestWorkerQueues"
125
- mock_get_celery_queues.return_value = {"celery": 4, "irrelevant": 5}
126
- job_class, _ = get_job_class_and_model(module, name)
127
- form = job_class().as_form()
128
- self.assertInHTML(
129
- """<tr><th><label for="id__task_queue">Task queue:</label></th>
130
- <td><select name="_task_queue" class="form-control" placeholder="Task queue" id="id__task_queue">
131
- <option value="celery">celery (4 workers)</option>
132
- <option value="nonexistent">nonexistent (0 workers)</option></select><br>
133
- <span class="helptext">The task queue to route this job to</span>
134
- <input type="hidden" name="_profile" value="False" id="id__profile"></td></tr>""",
135
- form.as_table(),
136
- )
120
+ def test_job_task_queues_setter(self):
121
+ """Test the task_queues property setter on Job."""
122
+ module = "dry_run"
123
+ name = "TestDryRun"
124
+ _, job_model = get_job_class_and_model(module, name)
125
+
126
+ invalid_queue = "Invalid job Queue"
127
+ with self.assertRaises(ValidationError) as cm:
128
+ job_model.task_queues = [invalid_queue]
129
+ job_model.validated_save()
130
+ self.assertIn(f"Job Queue {invalid_queue} does not exist in the database.", str(cm.exception))
137
131
 
138
- @mock.patch("nautobot.extras.utils.get_celery_queues")
139
- def test_job_class_task_queues_override(self, mock_get_celery_queues):
132
+ def test_job_class_job_queues(self):
140
133
  """
141
- Test job form with custom task queues defined on the job class and overridden on the model
134
+ Test job form with custom task queues defined on the job class
142
135
  """
143
136
  module = "task_queues"
144
137
  name = "TestWorkerQueues"
145
- mock_get_celery_queues.return_value = {"default": 1, "irrelevant": 5}
146
138
  job_class, job_model = get_job_class_and_model(module, name)
147
- job_model.task_queues = ["default", "priority"]
148
- job_model.task_queues_override = True
149
- job_model.save()
139
+ jq_1, _ = models.JobQueue.objects.get_or_create(
140
+ name="celery", defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
141
+ )
142
+ jq_2, _ = models.JobQueue.objects.get_or_create(
143
+ name="irrelevant", defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
144
+ )
145
+ job_model.job_queues.set([jq_1, jq_2])
150
146
  form = job_class().as_form()
151
- self.assertInHTML(
152
- """<tr><th><label for="id__task_queue">Task queue:</label></th>
153
- <td><select name="_task_queue" class="form-control" placeholder="Task queue" id="id__task_queue">
154
- <option value="default">default (1 worker)</option>
155
- <option value="priority">priority (0 workers)</option>
156
- </select><br><span class="helptext">The task queue to route this job to</span>
157
- <input type="hidden" name="_profile" value="False" id="id__profile"></td></tr>""",
158
- form.as_table(),
147
+ self.assertQuerySetEqual(
148
+ form.fields["_job_queue"].queryset,
149
+ models.JobQueue.objects.filter(jobs=job_model),
159
150
  )
160
151
 
161
152
  def test_supports_dryrun(self):
@@ -170,7 +161,7 @@ class JobTest(TestCase):
170
161
  self.assertTrue(job_model.supports_dryrun)
171
162
 
172
163
  module = "pass"
173
- name = "TestPassJob"
164
+ name = "TestPass"
174
165
  job_class, job_model = get_job_class_and_model(module, name)
175
166
  self.assertFalse(job_class.supports_dryrun)
176
167
  self.assertFalse(job_model.supports_dryrun)
@@ -212,7 +203,7 @@ register_jobs(MyJob)
212
203
  self.assertIsNotNone(get_job("my_jobs.MyJob"))
213
204
  # Also make sure some representative previous JOBS_ROOT jobs aren't still around:
214
205
  self.assertNotIn("dry_run.TestDryRun", jobs_data.keys())
215
- self.assertNotIn("pass.TestPassJob", jobs_data.keys())
206
+ self.assertNotIn("pass.TestPass", jobs_data.keys())
216
207
 
217
208
  # Create a second Job in the same module
218
209
  with open(os.path.join(temp_dir, "my_jobs.py"), "a") as fd:
@@ -319,7 +310,7 @@ class JobTransactionTest(TransactionTestCase):
319
310
  Job test with pass result.
320
311
  """
321
312
  module = "pass"
322
- name = "TestPassJob"
313
+ name = "TestPass"
323
314
  job_result = create_job_result_and_run_job(module, name)
324
315
  self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
325
316
  self.assertEqual(job_result.result, True)
@@ -353,7 +344,7 @@ class JobTransactionTest(TransactionTestCase):
353
344
  Job test with fail result.
354
345
  """
355
346
  module = "fail"
356
- name = "TestFailJob"
347
+ name = "TestFail"
357
348
  job_result = create_job_result_and_run_job(module, name)
358
349
  self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
359
350
  logs = job_result.job_log_entries
@@ -588,7 +579,7 @@ class JobTransactionTest(TransactionTestCase):
588
579
  Job test to see if the latest_result property is indeed returning the most recent job result
589
580
  """
590
581
  module = "pass"
591
- name = "TestPassJob"
582
+ name = "TestPass"
592
583
  job_result_1 = create_job_result_and_run_job(module, name)
593
584
  self.assertEqual(job_result_1.status, JobResultStatusChoices.STATUS_SUCCESS)
594
585
  job_result_2 = create_job_result_and_run_job(module, name)
@@ -803,7 +794,7 @@ class RunJobManagementCommandTest(TransactionTestCase):
803
794
  def test_runjob_nochange_successful(self):
804
795
  """Basic success-path test for Jobs that don't modify the Nautobot database."""
805
796
  module = "pass"
806
- name = "TestPassJob"
797
+ name = "TestPass"
807
798
  _job_class, job_model = get_job_class_and_model(module, name)
808
799
 
809
800
  out, err = self.run_command("--local", "--no-color", "--username", self.user.username, job_model.class_path)
@@ -890,9 +881,7 @@ class JobButtonReceiverTest(TestCase):
890
881
  name = "TestJobButtonReceiverSimple"
891
882
  job_class, _job_model = get_job_class_and_model(module, name)
892
883
  form = job_class().as_form()
893
- self.assertSequenceEqual(
894
- list(form.fields.keys()), ["object_pk", "object_model_name", "_task_queue", "_profile"]
895
- )
884
+ self.assertSequenceEqual(list(form.fields.keys()), ["object_pk", "object_model_name", "_job_queue", "_profile"])
896
885
 
897
886
  def test_hidden(self):
898
887
  module = "job_button_receiver"
@@ -903,7 +892,7 @@ class JobButtonReceiverTest(TestCase):
903
892
  def test_is_job_button(self):
904
893
  with self.subTest(expected=False):
905
894
  module = "pass"
906
- name = "TestPassJob"
895
+ name = "TestPass"
907
896
  _job_class, job_model = get_job_class_and_model(module, name)
908
897
  self.assertFalse(job_model.is_job_button_receiver)
909
898
 
@@ -955,7 +944,7 @@ class JobHookReceiverTest(TestCase):
955
944
  name = "TestJobHookReceiverLog"
956
945
  job_class, _job_model = get_job_class_and_model(module, name)
957
946
  form = job_class().as_form()
958
- self.assertSequenceEqual(list(form.fields.keys()), ["object_change", "_task_queue", "_profile"])
947
+ self.assertSequenceEqual(list(form.fields.keys()), ["object_change", "_job_queue", "_profile"])
959
948
 
960
949
  def test_hidden(self):
961
950
  module = "job_hook_receiver"
@@ -966,7 +955,7 @@ class JobHookReceiverTest(TestCase):
966
955
  def test_is_job_hook(self):
967
956
  with self.subTest(expected=False):
968
957
  module = "pass"
969
- name = "TestPassJob"
958
+ name = "TestPass"
970
959
  _job_class, job_model = get_job_class_and_model(module, name)
971
960
  self.assertFalse(job_model.is_job_hook_receiver)
972
961
 
@@ -1090,7 +1079,7 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
1090
1079
  ("info", "action: create"),
1091
1080
  ("info", f"jobresult.user: {self.user.username}"),
1092
1081
  ("info", "Test Job Hook Location 1"),
1093
- ("info", "Job completed"),
1082
+ ("success", "Job completed"),
1094
1083
  ]
1095
1084
  log_messages = models.JobLogEntry.objects.filter(job_result=job_result).values_list("log_level", "message")
1096
1085
  self.assertSequenceEqual(log_messages, expected_log_messages)
@@ -1116,7 +1105,7 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
1116
1105
  ("info", "action: update"),
1117
1106
  ("info", f"jobresult.user: {self.user.username}"),
1118
1107
  ("info", "Test Job Hook Location 1"),
1119
- ("info", "Job completed"),
1108
+ ("success", "Job completed"),
1120
1109
  ]
1121
1110
  log_messages = models.JobLogEntry.objects.filter(job_result=job_result).values_list("log_level", "message")
1122
1111
  self.assertSequenceEqual(log_messages, expected_log_messages)
@@ -1127,7 +1116,7 @@ class RemoveScheduledJobManagementCommandTestCase(TestCase):
1127
1116
  for i in range(1, 7):
1128
1117
  models.ScheduledJob.objects.create(
1129
1118
  name=f"test{i}",
1130
- task="pass.TestPassJob",
1119
+ task="pass.TestPass",
1131
1120
  interval=JobExecutionType.TYPE_FUTURE,
1132
1121
  user=self.user,
1133
1122
  start_time=timezone.now() - datetime.timedelta(days=i * 30),
@@ -1136,7 +1125,7 @@ class RemoveScheduledJobManagementCommandTestCase(TestCase):
1136
1125
 
1137
1126
  models.ScheduledJob.objects.create(
1138
1127
  name="test7",
1139
- task="pass.TestPassJob",
1128
+ task="pass.TestPass",
1140
1129
  interval=JobExecutionType.TYPE_DAILY,
1141
1130
  user=self.user,
1142
1131
  start_time=timezone.now() - datetime.timedelta(days=180),
@@ -1164,7 +1153,7 @@ class ScheduledJobIntervalTestCase(TestCase):
1164
1153
  start_time = timezone.now() + datetime.timedelta(days=6)
1165
1154
  scheduled_job = models.ScheduledJob.objects.create(
1166
1155
  name="weekly_interval",
1167
- task="pass.TestPassJob",
1156
+ task="pass.TestPass",
1168
1157
  interval=JobExecutionType.TYPE_WEEKLY,
1169
1158
  user=self.user,
1170
1159
  start_time=start_time,