nautobot 2.3.15b1__py3-none-any.whl → 2.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (785) hide show
  1. nautobot/__init__.py +15 -0
  2. nautobot/apps/__init__.py +1 -1
  3. nautobot/apps/api.py +8 -10
  4. nautobot/apps/change_logging.py +2 -2
  5. nautobot/apps/choices.py +4 -4
  6. nautobot/apps/config.py +32 -3
  7. nautobot/apps/events.py +19 -0
  8. nautobot/apps/exceptions.py +0 -2
  9. nautobot/apps/factory.py +2 -2
  10. nautobot/apps/filters.py +1 -1
  11. nautobot/apps/forms.py +20 -20
  12. nautobot/apps/graphql.py +2 -2
  13. nautobot/apps/jobs.py +8 -8
  14. nautobot/apps/models.py +19 -19
  15. nautobot/apps/tables.py +1 -1
  16. nautobot/apps/testing.py +10 -10
  17. nautobot/apps/ui.py +44 -9
  18. nautobot/apps/utils.py +7 -15
  19. nautobot/apps/views.py +8 -6
  20. nautobot/circuits/api/serializers.py +1 -0
  21. nautobot/circuits/api/views.py +4 -8
  22. nautobot/circuits/navigation.py +0 -57
  23. nautobot/circuits/templates/circuits/circuit_create.html +1 -7
  24. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
  25. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
  26. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
  27. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
  28. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
  29. nautobot/circuits/tests/integration/test_relationships.py +33 -24
  30. nautobot/circuits/tests/test_filters.py +4 -8
  31. nautobot/circuits/views.py +143 -26
  32. nautobot/cloud/api/views.py +6 -10
  33. nautobot/cloud/factory.py +4 -1
  34. nautobot/cloud/models.py +1 -1
  35. nautobot/cloud/tests/test_filters.py +5 -4
  36. nautobot/cloud/views.py +0 -16
  37. nautobot/core/api/constants.py +11 -0
  38. nautobot/core/api/fields.py +5 -5
  39. nautobot/core/api/filter_backends.py +3 -9
  40. nautobot/core/api/metadata.py +28 -256
  41. nautobot/core/api/pagination.py +3 -2
  42. nautobot/core/api/renderers.py +3 -0
  43. nautobot/core/api/schema.py +13 -2
  44. nautobot/core/api/serializers.py +54 -268
  45. nautobot/core/api/urls.py +3 -4
  46. nautobot/core/api/utils.py +0 -62
  47. nautobot/core/api/views.py +102 -159
  48. nautobot/core/apps/__init__.py +22 -575
  49. nautobot/core/celery/__init__.py +13 -0
  50. nautobot/core/celery/schedulers.py +48 -3
  51. nautobot/core/choices.py +2 -2
  52. nautobot/core/cli/__init__.py +8 -0
  53. nautobot/core/constants.py +7 -0
  54. nautobot/core/events/__init__.py +116 -0
  55. nautobot/core/events/base.py +27 -0
  56. nautobot/core/events/exceptions.py +10 -0
  57. nautobot/core/events/redis_broker.py +48 -0
  58. nautobot/core/events/syslog_broker.py +19 -0
  59. nautobot/core/exceptions.py +0 -6
  60. nautobot/core/filters.py +19 -16
  61. nautobot/core/forms/__init__.py +19 -19
  62. nautobot/core/forms/fields.py +62 -14
  63. nautobot/core/forms/forms.py +33 -2
  64. nautobot/core/forms/utils.py +2 -1
  65. nautobot/core/graphql/schema.py +3 -1
  66. nautobot/core/graphql/types.py +1 -1
  67. nautobot/core/jobs/__init__.py +28 -7
  68. nautobot/core/jobs/bulk_actions.py +248 -0
  69. nautobot/core/jobs/cleanup.py +2 -2
  70. nautobot/core/jobs/groups.py +1 -1
  71. nautobot/core/management/commands/generate_test_data.py +21 -0
  72. nautobot/core/management/commands/validate_models.py +1 -1
  73. nautobot/core/middleware.py +16 -0
  74. nautobot/core/models/__init__.py +1 -1
  75. nautobot/core/models/fields.py +11 -7
  76. nautobot/core/models/query_functions.py +2 -2
  77. nautobot/core/models/tree_queries.py +2 -2
  78. nautobot/core/settings.py +71 -4
  79. nautobot/core/settings.yaml +107 -0
  80. nautobot/core/tables.py +15 -51
  81. nautobot/core/tasks.py +1 -1
  82. nautobot/core/templates/about.html +67 -0
  83. nautobot/core/templates/components/button/default.html +7 -0
  84. nautobot/core/templates/components/button/dropdown.html +20 -0
  85. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  86. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  87. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  88. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  89. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  90. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  91. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  92. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  93. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  94. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  95. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  96. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  97. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  98. nautobot/core/templates/components/panel/panel.html +16 -0
  99. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  100. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  101. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  102. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  103. nautobot/core/templates/generic/object_retrieve.html +28 -17
  104. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  105. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  106. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  107. nautobot/core/templates/inc/footer.html +1 -0
  108. nautobot/core/templates/inc/media.html +3 -0
  109. nautobot/core/templates/inc/nav_menu.html +2 -1
  110. nautobot/core/templates/inc/relationships_panel.html +1 -1
  111. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  112. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  113. nautobot/core/templates/nautobot_config.py.j2 +16 -0
  114. nautobot/core/templates/panel_table.html +12 -0
  115. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  116. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  117. nautobot/core/templates/utilities/theme_preview.html +7 -0
  118. nautobot/core/templatetags/helpers.py +104 -6
  119. nautobot/core/templatetags/ui_framework.py +40 -0
  120. nautobot/core/testing/__init__.py +8 -8
  121. nautobot/core/testing/api.py +187 -137
  122. nautobot/core/testing/context.py +18 -0
  123. nautobot/core/testing/filters.py +44 -34
  124. nautobot/core/testing/forms.py +2 -0
  125. nautobot/core/testing/views.py +68 -146
  126. nautobot/core/tests/integration/test_app_home.py +0 -1
  127. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  128. nautobot/core/tests/integration/test_filters.py +0 -2
  129. nautobot/core/tests/integration/test_home.py +0 -1
  130. nautobot/core/tests/integration/test_navbar.py +0 -1
  131. nautobot/core/tests/integration/test_view_authentication.py +2 -1
  132. nautobot/core/tests/nautobot_config.py +198 -0
  133. nautobot/core/tests/runner.py +3 -3
  134. nautobot/core/tests/test_api.py +154 -176
  135. nautobot/core/tests/test_events.py +214 -0
  136. nautobot/core/tests/test_forms.py +1 -0
  137. nautobot/core/tests/test_jinja_filters.py +1 -0
  138. nautobot/core/tests/test_jobs.py +387 -14
  139. nautobot/core/tests/test_navigations.py +7 -241
  140. nautobot/core/tests/test_settings_schema.py +7 -0
  141. nautobot/core/tests/test_tables.py +100 -0
  142. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  143. nautobot/core/tests/test_ui.py +150 -0
  144. nautobot/core/tests/test_utils.py +55 -18
  145. nautobot/core/tests/test_views.py +153 -5
  146. nautobot/core/ui/__init__.py +0 -0
  147. nautobot/core/ui/base.py +11 -0
  148. nautobot/core/ui/choices.py +44 -0
  149. nautobot/core/ui/homepage.py +167 -0
  150. nautobot/core/ui/nav.py +280 -0
  151. nautobot/core/ui/object_detail.py +1855 -0
  152. nautobot/core/ui/utils.py +36 -0
  153. nautobot/core/urls.py +15 -0
  154. nautobot/core/utils/config.py +30 -3
  155. nautobot/core/utils/lookup.py +12 -2
  156. nautobot/core/utils/querysets.py +64 -0
  157. nautobot/core/utils/requests.py +24 -9
  158. nautobot/core/views/__init__.py +48 -1
  159. nautobot/core/views/generic.py +82 -177
  160. nautobot/core/views/mixins.py +98 -38
  161. nautobot/core/views/paginator.py +8 -5
  162. nautobot/core/views/renderers.py +9 -9
  163. nautobot/core/views/utils.py +11 -0
  164. nautobot/core/wsgi.py +3 -3
  165. nautobot/dcim/api/serializers.py +82 -189
  166. nautobot/dcim/api/urls.py +5 -0
  167. nautobot/dcim/api/views.py +57 -110
  168. nautobot/dcim/apps.py +1 -0
  169. nautobot/dcim/choices.py +28 -0
  170. nautobot/dcim/factory.py +58 -0
  171. nautobot/dcim/filters/__init__.py +204 -2
  172. nautobot/dcim/forms.py +221 -9
  173. nautobot/dcim/graphql/types.py +2 -2
  174. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  175. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  176. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  177. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  178. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  179. nautobot/dcim/models/__init__.py +5 -1
  180. nautobot/dcim/models/device_component_templates.py +2 -2
  181. nautobot/dcim/models/device_components.py +22 -20
  182. nautobot/dcim/models/devices.py +181 -3
  183. nautobot/dcim/models/locations.py +3 -3
  184. nautobot/dcim/models/power.py +6 -5
  185. nautobot/dcim/models/racks.py +6 -6
  186. nautobot/dcim/navigation.py +25 -224
  187. nautobot/dcim/signals.py +44 -0
  188. nautobot/dcim/tables/__init__.py +5 -3
  189. nautobot/dcim/tables/devices.py +103 -7
  190. nautobot/dcim/tables/devicetypes.py +2 -2
  191. nautobot/dcim/tables/racks.py +1 -1
  192. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  193. nautobot/dcim/templates/dcim/controller_create.html +2 -7
  194. nautobot/dcim/templates/dcim/controller_retrieve.html +6 -10
  195. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  196. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -0
  197. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +51 -0
  198. nautobot/dcim/templates/dcim/device/base.html +6 -42
  199. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  200. nautobot/dcim/templates/dcim/device.html +4 -10
  201. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  202. nautobot/dcim/templates/dcim/interface.html +1 -0
  203. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  204. nautobot/dcim/templates/dcim/location.html +1 -9
  205. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  206. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  207. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  208. nautobot/dcim/templates/dcim/rack.html +1 -9
  209. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  210. nautobot/dcim/templates/dcim/rackreservation.html +1 -9
  211. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +68 -0
  212. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +28 -0
  213. nautobot/dcim/tests/integration/test_controller.py +62 -0
  214. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  215. nautobot/dcim/tests/test_api.py +188 -64
  216. nautobot/dcim/tests/test_filters.py +172 -76
  217. nautobot/dcim/tests/test_graphql.py +52 -0
  218. nautobot/dcim/tests/test_jobs.py +118 -0
  219. nautobot/dcim/tests/test_models.py +159 -5
  220. nautobot/dcim/tests/test_signals.py +1 -0
  221. nautobot/dcim/tests/test_views.py +118 -88
  222. nautobot/dcim/urls.py +72 -27
  223. nautobot/dcim/utils.py +2 -2
  224. nautobot/dcim/views.py +357 -62
  225. nautobot/extras/api/customfields.py +2 -2
  226. nautobot/extras/api/serializers.py +111 -87
  227. nautobot/extras/api/urls.py +4 -0
  228. nautobot/extras/api/views.py +93 -35
  229. nautobot/extras/choices.py +13 -0
  230. nautobot/extras/constants.py +2 -1
  231. nautobot/extras/context_managers.py +23 -6
  232. nautobot/extras/datasources/git.py +4 -1
  233. nautobot/extras/factory.py +27 -0
  234. nautobot/extras/filters/__init__.py +66 -5
  235. nautobot/extras/forms/base.py +2 -2
  236. nautobot/extras/forms/forms.py +262 -59
  237. nautobot/extras/forms/mixins.py +3 -3
  238. nautobot/extras/graphql/types.py +25 -1
  239. nautobot/extras/health_checks.py +1 -2
  240. nautobot/extras/jobs.py +114 -20
  241. nautobot/extras/management/__init__.py +1 -0
  242. nautobot/extras/management/commands/runjob.py +7 -79
  243. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  244. nautobot/extras/management/utils.py +87 -0
  245. nautobot/extras/managers.py +3 -1
  246. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  247. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  248. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  249. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  250. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  251. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  252. nautobot/extras/models/__init__.py +5 -1
  253. nautobot/extras/models/change_logging.py +7 -3
  254. nautobot/extras/models/contacts.py +1 -1
  255. nautobot/extras/models/customfields.py +12 -11
  256. nautobot/extras/models/groups.py +11 -9
  257. nautobot/extras/models/jobs.py +237 -37
  258. nautobot/extras/models/models.py +2 -2
  259. nautobot/extras/models/relationships.py +69 -1
  260. nautobot/extras/models/secrets.py +5 -0
  261. nautobot/extras/navigation.py +20 -262
  262. nautobot/extras/plugins/__init__.py +54 -19
  263. nautobot/extras/plugins/marketplace_manifest.yml +455 -0
  264. nautobot/extras/plugins/tables.py +16 -14
  265. nautobot/extras/plugins/urls.py +1 -0
  266. nautobot/extras/plugins/views.py +104 -61
  267. nautobot/extras/registry.py +1 -1
  268. nautobot/extras/secrets/__init__.py +2 -2
  269. nautobot/extras/signals.py +39 -1
  270. nautobot/extras/tables.py +42 -6
  271. nautobot/extras/templates/extras/dynamicgroup.html +1 -9
  272. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  273. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  274. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  275. nautobot/extras/templates/extras/job_detail.html +52 -6
  276. nautobot/extras/templates/extras/job_edit.html +6 -2
  277. nautobot/extras/templates/extras/job_list.html +2 -7
  278. nautobot/extras/templates/extras/jobqueue_retrieve.html +36 -0
  279. nautobot/extras/templates/extras/marketplace.html +296 -0
  280. nautobot/extras/templates/extras/plugin_detail.html +32 -15
  281. nautobot/extras/templates/extras/plugins_list.html +35 -1
  282. nautobot/extras/templates/extras/plugins_tiles.html +90 -0
  283. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  284. nautobot/extras/templates/extras/secret.html +0 -65
  285. nautobot/extras/templates/extras/secret_check.js +16 -0
  286. nautobot/extras/templates/extras/secret_create.html +114 -0
  287. nautobot/extras/templates/extras/secret_edit.html +1 -114
  288. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  289. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  290. nautobot/extras/templatetags/job_buttons.py +5 -4
  291. nautobot/extras/templatetags/plugins.py +69 -6
  292. nautobot/extras/test_jobs/api_test_job.py +1 -1
  293. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  294. nautobot/extras/test_jobs/dry_run.py +1 -1
  295. nautobot/extras/test_jobs/fail.py +5 -5
  296. nautobot/extras/test_jobs/file_output.py +1 -1
  297. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  298. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  299. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  300. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  301. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  302. nautobot/extras/test_jobs/log_redaction.py +1 -1
  303. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  304. nautobot/extras/test_jobs/modify_db.py +1 -1
  305. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  306. nautobot/extras/test_jobs/object_var_required.py +1 -1
  307. nautobot/extras/test_jobs/object_vars.py +1 -1
  308. nautobot/extras/test_jobs/pass.py +3 -3
  309. nautobot/extras/test_jobs/profiling.py +1 -1
  310. nautobot/extras/test_jobs/relative_import.py +3 -3
  311. nautobot/extras/test_jobs/singleton.py +16 -0
  312. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  313. nautobot/extras/test_jobs/task_queues.py +1 -1
  314. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  315. nautobot/extras/tests/test_api.py +157 -55
  316. nautobot/extras/tests/test_context_managers.py +4 -1
  317. nautobot/extras/tests/test_customfields.py +1 -1
  318. nautobot/extras/tests/test_datasources.py +2 -1
  319. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  320. nautobot/extras/tests/test_filters.py +219 -535
  321. nautobot/extras/tests/test_forms.py +20 -1
  322. nautobot/extras/tests/test_job_variables.py +73 -152
  323. nautobot/extras/tests/test_jobs.py +192 -62
  324. nautobot/extras/tests/test_models.py +71 -16
  325. nautobot/extras/tests/test_plugins.py +62 -9
  326. nautobot/extras/tests/test_relationships.py +124 -10
  327. nautobot/extras/tests/test_utils.py +23 -2
  328. nautobot/extras/tests/test_views.py +162 -161
  329. nautobot/extras/tests/test_webhooks.py +2 -1
  330. nautobot/extras/urls.py +2 -20
  331. nautobot/extras/utils.py +119 -4
  332. nautobot/extras/views.py +188 -141
  333. nautobot/extras/webhooks.py +5 -2
  334. nautobot/ipam/api/fields.py +3 -3
  335. nautobot/ipam/api/serializers.py +44 -137
  336. nautobot/ipam/api/views.py +68 -110
  337. nautobot/ipam/factory.py +1 -1
  338. nautobot/ipam/filters.py +3 -2
  339. nautobot/ipam/models.py +10 -12
  340. nautobot/ipam/navigation.py +0 -90
  341. nautobot/ipam/querysets.py +2 -2
  342. nautobot/ipam/tables.py +3 -1
  343. nautobot/ipam/templates/ipam/ipaddress.html +1 -9
  344. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  345. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  346. nautobot/ipam/templates/ipam/prefix.html +1 -9
  347. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  348. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  349. nautobot/ipam/templates/ipam/vlan.html +1 -9
  350. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  351. nautobot/ipam/templates/ipam/vrf.html +0 -47
  352. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  353. nautobot/ipam/tests/test_api.py +19 -6
  354. nautobot/ipam/tests/test_filters.py +39 -119
  355. nautobot/ipam/tests/test_forms.py +49 -47
  356. nautobot/ipam/tests/test_migrations.py +30 -30
  357. nautobot/ipam/tests/test_models.py +56 -36
  358. nautobot/ipam/tests/test_querysets.py +14 -0
  359. nautobot/ipam/tests/test_views.py +3 -0
  360. nautobot/ipam/urls.py +3 -69
  361. nautobot/ipam/utils/__init__.py +16 -10
  362. nautobot/ipam/views.py +91 -162
  363. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  364. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  365. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  366. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  367. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  368. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  369. nautobot/project-static/css/base.css +38 -3
  370. nautobot/project-static/docs/404.html +463 -19
  371. nautobot/project-static/docs/apps/index.html +463 -19
  372. nautobot/project-static/docs/apps/nautobot-apps.html +464 -21
  373. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  374. nautobot/project-static/docs/assets/extra.css +5 -1
  375. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
  376. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
  377. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +479 -25
  378. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +476 -22
  379. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +792 -291
  380. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +507 -33
  381. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +512 -36
  382. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +473 -22
  383. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +469 -20
  384. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +499 -35
  385. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9883 -0
  386. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +525 -77
  387. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +548 -53
  388. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +672 -96
  389. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1033 -180
  390. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +526 -51
  391. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +876 -190
  392. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +957 -237
  393. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +477 -23
  394. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +488 -30
  395. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +663 -101
  396. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +949 -481
  397. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6427 -1236
  398. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +476 -22
  399. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +879 -346
  400. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +830 -173
  401. nautobot/project-static/docs/development/apps/api/configuration-view.html +463 -19
  402. nautobot/project-static/docs/development/apps/api/database-backend-config.html +463 -19
  403. nautobot/project-static/docs/development/apps/api/models/django-admin.html +463 -19
  404. nautobot/project-static/docs/development/apps/api/models/global-search.html +463 -19
  405. nautobot/project-static/docs/development/apps/api/models/graphql.html +463 -19
  406. nautobot/project-static/docs/development/apps/api/models/index.html +463 -19
  407. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +463 -19
  408. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +463 -19
  409. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +463 -19
  410. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +463 -19
  411. nautobot/project-static/docs/development/apps/api/platform-features/index.html +463 -19
  412. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +463 -19
  413. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +463 -19
  414. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +463 -19
  415. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +463 -19
  416. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +463 -19
  417. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +463 -19
  418. nautobot/project-static/docs/development/apps/api/prometheus.html +463 -19
  419. nautobot/project-static/docs/development/apps/api/setup.html +467 -155
  420. nautobot/project-static/docs/development/apps/api/testing.html +463 -19
  421. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +463 -19
  422. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +463 -19
  423. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +463 -19
  424. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +463 -19
  425. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +743 -130
  426. nautobot/project-static/docs/development/apps/api/views/base-template.html +463 -19
  427. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +463 -19
  428. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +463 -19
  429. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +463 -19
  430. nautobot/project-static/docs/development/apps/api/views/index.html +465 -20
  431. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +467 -19
  432. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +493 -19
  433. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +463 -19
  434. nautobot/project-static/docs/development/apps/api/views/notes.html +463 -19
  435. nautobot/project-static/docs/development/apps/api/views/rest-api.html +469 -21
  436. nautobot/project-static/docs/development/apps/api/views/urls.html +463 -19
  437. nautobot/project-static/docs/development/apps/index.html +463 -19
  438. nautobot/project-static/docs/development/apps/migration/code-updates.html +464 -52
  439. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +464 -20
  440. nautobot/project-static/docs/development/apps/migration/from-v1.html +463 -19
  441. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +463 -19
  442. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +463 -19
  443. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +463 -19
  444. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +466 -22
  445. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9261 -0
  446. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9375 -0
  447. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9671 -0
  448. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9559 -0
  449. nautobot/project-static/docs/development/apps/porting-from-netbox.html +466 -22
  450. nautobot/project-static/docs/development/core/application-registry.html +463 -19
  451. nautobot/project-static/docs/development/core/best-practices.html +463 -19
  452. nautobot/project-static/docs/development/core/bootstrap-ui.html +463 -19
  453. nautobot/project-static/docs/development/core/caching.html +463 -19
  454. nautobot/project-static/docs/development/core/controllers.html +465 -19
  455. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +491 -90
  456. nautobot/project-static/docs/development/core/generic-views.html +463 -19
  457. nautobot/project-static/docs/development/core/getting-started.html +541 -129
  458. nautobot/project-static/docs/development/core/homepage.html +474 -30
  459. nautobot/project-static/docs/development/core/index.html +463 -19
  460. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9754 -0
  461. nautobot/project-static/docs/development/core/model-checklist.html +473 -27
  462. nautobot/project-static/docs/development/core/model-features.html +463 -19
  463. nautobot/project-static/docs/development/core/natural-keys.html +463 -19
  464. nautobot/project-static/docs/development/core/navigation-menu.html +480 -26
  465. nautobot/project-static/docs/development/core/release-checklist.html +480 -48
  466. nautobot/project-static/docs/development/core/role-internals.html +463 -19
  467. nautobot/project-static/docs/development/core/settings.html +463 -19
  468. nautobot/project-static/docs/development/core/style-guide.html +466 -22
  469. nautobot/project-static/docs/development/core/templates.html +473 -22
  470. nautobot/project-static/docs/development/core/testing.html +463 -19
  471. nautobot/project-static/docs/development/core/ui-component-framework.html +11116 -0
  472. nautobot/project-static/docs/development/core/user-preferences.html +466 -22
  473. nautobot/project-static/docs/development/index.html +463 -19
  474. nautobot/project-static/docs/development/jobs/index.html +501 -21
  475. nautobot/project-static/docs/development/jobs/migration/from-v1.html +463 -19
  476. nautobot/project-static/docs/index.html +471 -38
  477. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  478. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  479. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  480. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  481. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  482. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  483. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  484. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  485. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  486. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  487. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  488. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  489. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  490. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  491. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  492. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  493. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  494. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  495. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  496. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  497. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  498. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  499. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  500. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  501. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  502. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  503. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  504. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  505. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  506. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  507. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  508. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  509. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  510. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  511. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  512. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  513. nautobot/project-static/docs/objects.inv +0 -0
  514. nautobot/project-static/docs/overview/application_stack.html +469 -23
  515. nautobot/project-static/docs/overview/design_philosophy.html +463 -19
  516. nautobot/project-static/docs/release-notes/index.html +485 -22
  517. nautobot/project-static/docs/release-notes/version-1.0.html +651 -208
  518. nautobot/project-static/docs/release-notes/version-1.1.html +648 -205
  519. nautobot/project-static/docs/release-notes/version-1.2.html +723 -280
  520. nautobot/project-static/docs/release-notes/version-1.3.html +749 -306
  521. nautobot/project-static/docs/release-notes/version-1.4.html +834 -392
  522. nautobot/project-static/docs/release-notes/version-1.5.html +1022 -581
  523. nautobot/project-static/docs/release-notes/version-1.6.html +942 -518
  524. nautobot/project-static/docs/release-notes/version-2.0.html +945 -504
  525. nautobot/project-static/docs/release-notes/version-2.1.html +780 -339
  526. nautobot/project-static/docs/release-notes/version-2.2.html +773 -332
  527. nautobot/project-static/docs/release-notes/version-2.3.html +1142 -448
  528. nautobot/project-static/docs/release-notes/version-2.4.html +10323 -0
  529. nautobot/project-static/docs/requirements.txt +2 -2
  530. nautobot/project-static/docs/search/search_index.json +1 -1
  531. nautobot/project-static/docs/sitemap.xml +342 -270
  532. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  533. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +463 -19
  534. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +463 -19
  535. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +463 -19
  536. nautobot/project-static/docs/user-guide/administration/configuration/index.html +475 -32
  537. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +463 -19
  538. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +894 -180
  539. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +463 -19
  540. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +463 -19
  541. nautobot/project-static/docs/user-guide/administration/guides/docker.html +476 -29
  542. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +463 -19
  543. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +463 -19
  544. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +463 -19
  545. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +463 -19
  546. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +463 -19
  547. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +465 -21
  548. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +463 -19
  549. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +463 -19
  550. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +463 -19
  551. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +483 -23
  552. nautobot/project-static/docs/user-guide/administration/installation/index.html +468 -20
  553. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +464 -20
  554. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +463 -19
  555. nautobot/project-static/docs/user-guide/administration/installation/services.html +463 -19
  556. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +463 -19
  557. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +484 -41
  558. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +477 -66
  559. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +477 -66
  560. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +463 -19
  561. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +463 -19
  562. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +463 -19
  563. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +463 -19
  564. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +463 -19
  565. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +466 -23
  566. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +463 -19
  567. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  568. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +463 -19
  569. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +469 -21
  570. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +463 -19
  571. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +463 -19
  572. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +463 -19
  573. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +463 -19
  574. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +463 -19
  575. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +463 -19
  576. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +463 -19
  577. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +463 -19
  578. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +463 -19
  579. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +463 -19
  580. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +463 -19
  581. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +463 -19
  582. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +463 -19
  583. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +463 -19
  584. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +463 -19
  585. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +463 -19
  586. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +463 -19
  587. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +499 -20
  588. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +489 -22
  589. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +463 -19
  590. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +463 -19
  591. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +463 -19
  592. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +463 -19
  593. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +463 -19
  594. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +463 -19
  595. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +463 -19
  596. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +463 -19
  597. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +463 -19
  598. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +463 -19
  599. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +463 -19
  600. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +463 -19
  601. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +463 -19
  602. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +463 -19
  603. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +463 -19
  604. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +463 -19
  605. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +463 -19
  606. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +463 -19
  607. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +463 -19
  608. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +463 -19
  609. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +463 -19
  610. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +463 -19
  611. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +463 -19
  612. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +463 -19
  613. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +463 -19
  614. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +463 -19
  615. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +463 -19
  616. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +463 -19
  617. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +463 -19
  618. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +463 -19
  619. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +463 -19
  620. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +463 -19
  621. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +463 -19
  622. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +463 -19
  623. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9375 -0
  624. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +470 -30
  625. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +463 -19
  626. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +463 -19
  627. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +463 -19
  628. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +463 -19
  629. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +463 -19
  630. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +463 -19
  631. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +463 -19
  632. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +463 -19
  633. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +463 -19
  634. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +463 -19
  635. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +463 -19
  636. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +463 -19
  637. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +466 -22
  638. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +463 -19
  639. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +463 -19
  640. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +463 -19
  641. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +463 -19
  642. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +463 -19
  643. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +463 -19
  644. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +466 -22
  645. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9313 -0
  646. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9217 -0
  647. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9211 -0
  648. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9277 -0
  649. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +463 -19
  650. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +463 -19
  651. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +463 -19
  652. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +463 -19
  653. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +463 -19
  654. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +463 -19
  655. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +463 -19
  656. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +463 -19
  657. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +463 -19
  658. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +463 -19
  659. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +463 -19
  660. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +468 -22
  661. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +463 -19
  662. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  663. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  664. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  665. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  666. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  667. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  668. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  669. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  670. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  671. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  672. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  673. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  674. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  675. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +463 -19
  676. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +463 -19
  677. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +466 -22
  678. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
  679. nautobot/project-static/docs/user-guide/index.html +463 -19
  680. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +466 -22
  681. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +467 -23
  682. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +463 -19
  683. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +463 -19
  684. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +463 -19
  685. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9617 -0
  686. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +466 -22
  687. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +463 -19
  688. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +463 -19
  689. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +463 -19
  690. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +463 -19
  691. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +463 -19
  692. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +472 -23
  693. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +466 -22
  694. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +466 -22
  695. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +463 -19
  696. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9224 -0
  697. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
  698. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +466 -22
  699. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +463 -19
  700. nautobot/project-static/docs/user-guide/platform-functionality/note.html +463 -19
  701. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +463 -19
  702. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +467 -23
  703. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9292 -0
  704. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +463 -19
  705. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +511 -40
  706. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +494 -23
  707. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +463 -19
  708. nautobot/project-static/docs/user-guide/platform-functionality/role.html +463 -19
  709. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +463 -19
  710. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +463 -19
  711. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +466 -22
  712. nautobot/project-static/docs/user-guide/platform-functionality/status.html +463 -19
  713. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +463 -19
  714. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +531 -56
  715. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +463 -19
  716. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +463 -19
  717. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +463 -19
  718. nautobot/project-static/img/jinja_logo.svg +97 -0
  719. nautobot/project-static/js/forms.js +6 -1
  720. nautobot/project-static/js/nav_menu.js +2 -1
  721. nautobot/tenancy/api/serializers.py +0 -2
  722. nautobot/tenancy/api/views.py +9 -13
  723. nautobot/tenancy/factory.py +1 -1
  724. nautobot/tenancy/navigation.py +0 -29
  725. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  726. nautobot/tenancy/tests/test_filters.py +29 -134
  727. nautobot/tenancy/views.py +35 -24
  728. nautobot/users/admin.py +3 -1
  729. nautobot/users/api/serializers.py +4 -4
  730. nautobot/users/api/views.py +3 -3
  731. nautobot/users/forms.py +19 -0
  732. nautobot/users/templates/users/preferences.html +22 -0
  733. nautobot/users/tests/test_filters.py +1 -19
  734. nautobot/users/tests/test_views.py +57 -0
  735. nautobot/users/utils.py +8 -0
  736. nautobot/users/views.py +48 -11
  737. nautobot/virtualization/api/serializers.py +4 -4
  738. nautobot/virtualization/api/views.py +5 -24
  739. nautobot/virtualization/filters.py +1 -2
  740. nautobot/virtualization/models.py +1 -1
  741. nautobot/virtualization/navigation.py +0 -48
  742. nautobot/virtualization/tables.py +2 -2
  743. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  744. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  745. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
  746. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  747. nautobot/virtualization/tests/test_filters.py +57 -166
  748. nautobot/virtualization/views.py +18 -15
  749. nautobot/wireless/__init__.py +0 -0
  750. nautobot/wireless/api/__init__.py +0 -0
  751. nautobot/wireless/api/serializers.py +44 -0
  752. nautobot/wireless/api/urls.py +20 -0
  753. nautobot/wireless/api/views.py +34 -0
  754. nautobot/wireless/apps.py +8 -0
  755. nautobot/wireless/choices.py +345 -0
  756. nautobot/wireless/factory.py +138 -0
  757. nautobot/wireless/filters.py +167 -0
  758. nautobot/wireless/forms.py +283 -0
  759. nautobot/wireless/homepage.py +19 -0
  760. nautobot/wireless/migrations/0001_initial.py +223 -0
  761. nautobot/wireless/migrations/__init__.py +0 -0
  762. nautobot/wireless/models.py +207 -0
  763. nautobot/wireless/navigation.py +105 -0
  764. nautobot/wireless/tables.py +244 -0
  765. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  766. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  767. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  768. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +56 -0
  769. nautobot/wireless/tests/__init__.py +0 -0
  770. nautobot/wireless/tests/integration/__init__.py +0 -0
  771. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  772. nautobot/wireless/tests/test_api.py +247 -0
  773. nautobot/wireless/tests/test_filters.py +82 -0
  774. nautobot/wireless/tests/test_models.py +22 -0
  775. nautobot/wireless/tests/test_views.py +378 -0
  776. nautobot/wireless/urls.py +13 -0
  777. nautobot/wireless/views.py +119 -0
  778. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/METADATA +11 -13
  779. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/RECORD +783 -613
  780. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/WHEEL +1 -1
  781. nautobot/core/fixtures/user-data.json +0 -59
  782. nautobot/core/utils/navigation.py +0 -54
  783. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/LICENSE.txt +0 -0
  784. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/NOTICE +0 -0
  785. {nautobot-2.3.15b1.dist-info → nautobot-2.4.0.dist-info}/entry_points.txt +0 -0
@@ -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,12 +31,15 @@ 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,
36
38
  )
37
39
  from nautobot.extras.context_managers import change_logging, JobHookChangeContext, web_request_context
38
40
  from nautobot.extras.jobs import get_job, get_jobs
41
+ from nautobot.extras.models import Job, JobQueue
42
+ from nautobot.extras.models.jobs import JobLogEntry
39
43
 
40
44
 
41
45
  class JobTest(TestCase):
@@ -71,7 +75,7 @@ class JobTest(TestCase):
71
75
  name = "TestFieldOrder"
72
76
  job_class = get_job(f"{module}.{name}")
73
77
  form = job_class().as_form()
74
- self.assertSequenceEqual(list(form.fields.keys()), ["var1", "var2", "var23", "_task_queue", "_profile"])
78
+ self.assertSequenceEqual(list(form.fields.keys()), ["var1", "var2", "var23", "_job_queue", "_profile"])
75
79
 
76
80
  def test_no_field_order(self):
77
81
  """
@@ -81,7 +85,7 @@ class JobTest(TestCase):
81
85
  name = "TestNoFieldOrder"
82
86
  job_class = get_job(f"{module}.{name}")
83
87
  form = job_class().as_form()
84
- self.assertSequenceEqual(list(form.fields.keys()), ["var23", "var2", "_task_queue", "_profile"])
88
+ self.assertSequenceEqual(list(form.fields.keys()), ["var23", "var2", "_job_queue", "_profile"])
85
89
 
86
90
  def test_no_field_order_inherited_variable(self):
87
91
  """
@@ -93,7 +97,7 @@ class JobTest(TestCase):
93
97
  form = job_class().as_form()
94
98
  self.assertSequenceEqual(
95
99
  list(form.fields.keys()),
96
- ["testvar1", "b_testvar2", "a_testvar3", "_task_queue", "_profile"],
100
+ ["testvar1", "b_testvar2", "a_testvar3", "_job_queue", "_profile"],
97
101
  )
98
102
 
99
103
  def test_dryrun_default(self):
@@ -115,47 +119,36 @@ class JobTest(TestCase):
115
119
  form = job_class().as_form()
116
120
  self.assertEqual(form.fields["dryrun"].initial, job_model.dryrun_default)
117
121
 
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
- )
122
+ def test_job_task_queues_setter(self):
123
+ """Test the task_queues property setter on Job."""
124
+ module = "dry_run"
125
+ name = "TestDryRun"
126
+ _, job_model = get_job_class_and_model(module, name)
127
+
128
+ invalid_queue = "Invalid job Queue"
129
+ with self.assertRaises(ValidationError) as cm:
130
+ job_model.task_queues = [invalid_queue]
131
+ job_model.validated_save()
132
+ self.assertIn(f"Job Queue {invalid_queue} does not exist in the database.", str(cm.exception))
137
133
 
138
- @mock.patch("nautobot.extras.utils.get_celery_queues")
139
- def test_job_class_task_queues_override(self, mock_get_celery_queues):
134
+ def test_job_class_job_queues(self):
140
135
  """
141
- Test job form with custom task queues defined on the job class and overridden on the model
136
+ Test job form with custom task queues defined on the job class
142
137
  """
143
138
  module = "task_queues"
144
139
  name = "TestWorkerQueues"
145
- mock_get_celery_queues.return_value = {"default": 1, "irrelevant": 5}
146
140
  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()
141
+ jq_1, _ = models.JobQueue.objects.get_or_create(
142
+ name="celery", defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
143
+ )
144
+ jq_2, _ = models.JobQueue.objects.get_or_create(
145
+ name="irrelevant", defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
146
+ )
147
+ job_model.job_queues.set([jq_1, jq_2])
150
148
  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(),
149
+ self.assertQuerySetEqual(
150
+ form.fields["_job_queue"].queryset,
151
+ models.JobQueue.objects.filter(jobs=job_model),
159
152
  )
160
153
 
161
154
  def test_supports_dryrun(self):
@@ -170,7 +163,7 @@ class JobTest(TestCase):
170
163
  self.assertTrue(job_model.supports_dryrun)
171
164
 
172
165
  module = "pass"
173
- name = "TestPass"
166
+ name = "TestPassJob"
174
167
  job_class, job_model = get_job_class_and_model(module, name)
175
168
  self.assertFalse(job_class.supports_dryrun)
176
169
  self.assertFalse(job_model.supports_dryrun)
@@ -200,27 +193,31 @@ class JobTest(TestCase):
200
193
  with override_settings(JOBS_ROOT=temp_dir):
201
194
  # Create a new Job and make sure it's discovered correctly
202
195
  with open(os.path.join(temp_dir, "my_jobs.py"), "w") as fd:
203
- fd.write("""\
196
+ fd.write(
197
+ """\
204
198
  from nautobot.apps.jobs import Job, register_jobs
205
199
  class MyJob(Job):
206
200
  def run(self):
207
201
  pass
208
202
  register_jobs(MyJob)
209
- """)
203
+ """
204
+ )
210
205
  jobs_data = get_jobs(reload=True)
211
206
  self.assertIn("my_jobs.MyJob", jobs_data.keys())
212
207
  self.assertIsNotNone(get_job("my_jobs.MyJob"))
213
208
  # Also make sure some representative previous JOBS_ROOT jobs aren't still around:
214
209
  self.assertNotIn("dry_run.TestDryRun", jobs_data.keys())
215
- self.assertNotIn("pass.TestPass", jobs_data.keys())
210
+ self.assertNotIn("pass.TestPassJob", jobs_data.keys())
216
211
 
217
212
  # Create a second Job in the same module
218
213
  with open(os.path.join(temp_dir, "my_jobs.py"), "a") as fd:
219
- fd.write("""
214
+ fd.write(
215
+ """
220
216
  class MyOtherJob(MyJob):
221
217
  pass
222
218
  register_jobs(MyOtherJob)
223
- """)
219
+ """
220
+ )
224
221
  jobs_data = get_jobs(reload=True)
225
222
  self.assertIn("my_jobs.MyJob", jobs_data.keys())
226
223
  self.assertIsNotNone(get_job("my_jobs.MyJob"))
@@ -229,14 +226,16 @@ register_jobs(MyOtherJob)
229
226
 
230
227
  # Create a third Job in another module
231
228
  with open(os.path.join(temp_dir, "their_jobs.py"), "w") as fd:
232
- fd.write("""
229
+ fd.write(
230
+ """
233
231
  from nautobot.apps.jobs import Job, register_jobs
234
232
 
235
233
  class MyJob(Job):
236
234
  def run(self):
237
235
  pass
238
236
  register_jobs(MyJob)
239
- """)
237
+ """
238
+ )
240
239
  jobs_data = get_jobs(reload=True)
241
240
  self.assertIn("my_jobs.MyJob", jobs_data.keys())
242
241
  self.assertIsNotNone(get_job("my_jobs.MyJob"))
@@ -258,14 +257,16 @@ register_jobs(MyJob)
258
257
 
259
258
  # Create a module with an inauspicious name
260
259
  with open(os.path.join(temp_dir, "traceback.py"), "w") as fd:
261
- fd.write("""
260
+ fd.write(
261
+ """
262
262
  from nautobot.apps.jobs import Job, register_jobs
263
263
 
264
264
  class BadJob(Job):
265
265
  def run(self):
266
266
  raise RuntimeError("You ran a bad job!")
267
267
  register_jobs(BadJob)
268
- """)
268
+ """
269
+ )
269
270
  jobs_data = get_jobs(reload=True)
270
271
  self.assertIn("my_jobs.MyJob", jobs_data.keys())
271
272
  self.assertIsNotNone(get_job("my_jobs.MyJob"))
@@ -297,6 +298,105 @@ class JobTransactionTest(TransactionTestCase):
297
298
  self.request.id = uuid.uuid4()
298
299
  self.request.user = self.user
299
300
 
301
+ def test_bulk_delete_system_jobs_fail(self):
302
+ system_job_queryset = Job.objects.filter(module_name__startswith="nautobot.")
303
+ pk_list = [str(pk) for pk in system_job_queryset.values_list("pk", flat=True)[:3]]
304
+ initial_count = Job.objects.all().count()
305
+ self.add_permissions("extras.delete_job")
306
+ job_result = create_job_result_and_run_job(
307
+ "nautobot.core.jobs.bulk_actions",
308
+ "BulkDeleteObjects",
309
+ content_type=ContentType.objects.get_for_model(Job).id,
310
+ delete_all=False,
311
+ filter_query_params={},
312
+ pk_list=pk_list,
313
+ username=self.user.username,
314
+ )
315
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
316
+ error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
317
+ self.assertIn(
318
+ f"Unable to delete Job {system_job_queryset.first()}. System Job cannot be deleted", error_log.message
319
+ )
320
+ self.assertEqual(initial_count, Job.objects.all().count())
321
+
322
+ def test_job_bulk_edit_default_queue_stays_default_after_one_field_update(self):
323
+ self.add_permissions("extras.change_job", "extras.view_job")
324
+ job_class_to_test = "TestPassJob"
325
+ job_description = "default job queue test"
326
+ job_to_test = Job.objects.get(job_class_name=job_class_to_test)
327
+ queryset = Job.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
328
+ default_job_queue = job_to_test.default_job_queue
329
+ pk_list = list(queryset.values_list("pk", flat=True))
330
+
331
+ job_ct = ContentType.objects.get_for_model(Job)
332
+ job_result = create_job_result_and_run_job(
333
+ "nautobot.core.jobs.bulk_actions",
334
+ "BulkEditObjects",
335
+ content_type=job_ct.id,
336
+ edit_all=False,
337
+ filter_query_params={},
338
+ form_data={
339
+ "pk": pk_list,
340
+ "description": job_description,
341
+ },
342
+ username=self.user.username,
343
+ )
344
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
345
+ self.assertEqual(Job.objects.filter(description=job_description).count(), queryset.count())
346
+ self.assertFalse(
347
+ JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_WARNING).exists()
348
+ )
349
+ self.assertFalse(
350
+ JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR).exists()
351
+ )
352
+ instance = Job.objects.get(job_class_name=job_class_to_test)
353
+ self.assertEqual(instance.description, job_description)
354
+ self.assertEqual(instance.default_job_queue, default_job_queue)
355
+
356
+ def test_job_bulk_edit_preserve_job_queues_after_one_field_update(self):
357
+ self.add_permissions("extras.change_job", "extras.view_job")
358
+ job_class_to_test = "TestPassJob"
359
+ job_description = "job queues test"
360
+ job_to_test = Job.objects.get(job_class_name=job_class_to_test)
361
+
362
+ # Simulate 3 job queues set
363
+ job_queues = JobQueue.objects.all()[:3]
364
+ job_to_test.job_queues.set(job_queues)
365
+ job_to_test.job_queues.add(job_to_test.default_job_queue)
366
+ job_to_test.job_queues_override = True
367
+ job_to_test.save()
368
+
369
+ expected_job_queues = list(job_to_test.job_queues.values_list("pk", flat=True))
370
+
371
+ queryset = Job.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
372
+ pk_list = list(queryset.values_list("pk", flat=True))
373
+
374
+ job_ct = ContentType.objects.get_for_model(Job)
375
+ job_result = create_job_result_and_run_job(
376
+ "nautobot.core.jobs.bulk_actions",
377
+ "BulkEditObjects",
378
+ content_type=job_ct.id,
379
+ edit_all=False,
380
+ filter_query_params={},
381
+ form_data={
382
+ "pk": pk_list,
383
+ "description": job_description,
384
+ },
385
+ username=self.user.username,
386
+ )
387
+ self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
388
+ self.assertEqual(Job.objects.filter(description=job_description).count(), queryset.count())
389
+ self.assertFalse(
390
+ JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_WARNING).exists()
391
+ )
392
+ self.assertFalse(
393
+ JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR).exists()
394
+ )
395
+
396
+ instance = Job.objects.get(job_class_name=job_class_to_test)
397
+ self.assertEqual(instance.description, job_description)
398
+ self.assertQuerySetEqual(instance.job_queues.all(), JobQueue.objects.filter(pk__in=expected_job_queues))
399
+
300
400
  def test_job_hard_time_limit_less_than_soft_time_limit(self):
301
401
  """
302
402
  Job test which produces a warning log message because the time_limit is less than the soft_time_limit.
@@ -319,7 +419,7 @@ class JobTransactionTest(TransactionTestCase):
319
419
  Job test with pass result.
320
420
  """
321
421
  module = "pass"
322
- name = "TestPass"
422
+ name = "TestPassJob"
323
423
  job_result = create_job_result_and_run_job(module, name)
324
424
  self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
325
425
  self.assertEqual(job_result.result, True)
@@ -353,7 +453,7 @@ class JobTransactionTest(TransactionTestCase):
353
453
  Job test with fail result.
354
454
  """
355
455
  module = "fail"
356
- name = "TestFail"
456
+ name = "TestFailJob"
357
457
  job_result = create_job_result_and_run_job(module, name)
358
458
  self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
359
459
  logs = job_result.job_log_entries
@@ -588,7 +688,7 @@ class JobTransactionTest(TransactionTestCase):
588
688
  Job test to see if the latest_result property is indeed returning the most recent job result
589
689
  """
590
690
  module = "pass"
591
- name = "TestPass"
691
+ name = "TestPassJob"
592
692
  job_result_1 = create_job_result_and_run_job(module, name)
593
693
  self.assertEqual(job_result_1.status, JobResultStatusChoices.STATUS_SUCCESS)
594
694
  job_result_2 = create_job_result_and_run_job(module, name)
@@ -615,6 +715,38 @@ class JobTransactionTest(TransactionTestCase):
615
715
  self.assertTrue(profiling_result.exists())
616
716
  profiling_result.unlink()
617
717
 
718
+ def test_job_singleton(self):
719
+ module = "singleton"
720
+ name = "TestSingletonJob"
721
+
722
+ job_class, _ = get_job_class_and_model(module, name, "local")
723
+ self.assertTrue(job_class.is_singleton)
724
+ cache.set(job_class.singleton_cache_key, 1)
725
+ failed_job_result = create_job_result_and_run_job(module, name)
726
+
727
+ self.assertEqual(
728
+ failed_job_result.status, JobResultStatusChoices.STATUS_FAILURE, msg="Duplicate singleton job didn't error."
729
+ )
730
+ self.assertIsNone(cache.get(job_class.singleton_cache_key, None))
731
+
732
+ def test_job_ignore_singleton(self):
733
+ module = "singleton"
734
+ name = "TestSingletonJob"
735
+
736
+ job_class, _ = get_job_class_and_model(module, name, "local")
737
+ self.assertTrue(job_class.is_singleton)
738
+ cache.set(job_class.singleton_cache_key, 1)
739
+ passed_job_result = create_job_result_and_run_job(
740
+ module, name, celery_kwargs={"nautobot_job_ignore_singleton_lock": True}
741
+ )
742
+
743
+ self.assertEqual(
744
+ passed_job_result.status,
745
+ JobResultStatusChoices.STATUS_SUCCESS,
746
+ msg="Duplicate singleton job didn't succeed with nautobot_job_ignore_singleton_lock=True.",
747
+ )
748
+ self.assertIsNone(cache.get(job_class.singleton_cache_key, None))
749
+
618
750
  @mock.patch("nautobot.extras.context_managers.enqueue_webhooks")
619
751
  def test_job_fires_webhooks(self, mock_enqueue_webhooks):
620
752
  module = "atomic_transaction"
@@ -803,7 +935,7 @@ class RunJobManagementCommandTest(TransactionTestCase):
803
935
  def test_runjob_nochange_successful(self):
804
936
  """Basic success-path test for Jobs that don't modify the Nautobot database."""
805
937
  module = "pass"
806
- name = "TestPass"
938
+ name = "TestPassJob"
807
939
  _job_class, job_model = get_job_class_and_model(module, name)
808
940
 
809
941
  out, err = self.run_command("--local", "--no-color", "--username", self.user.username, job_model.class_path)
@@ -890,9 +1022,7 @@ class JobButtonReceiverTest(TestCase):
890
1022
  name = "TestJobButtonReceiverSimple"
891
1023
  job_class, _job_model = get_job_class_and_model(module, name)
892
1024
  form = job_class().as_form()
893
- self.assertSequenceEqual(
894
- list(form.fields.keys()), ["object_pk", "object_model_name", "_task_queue", "_profile"]
895
- )
1025
+ self.assertSequenceEqual(list(form.fields.keys()), ["object_pk", "object_model_name", "_job_queue", "_profile"])
896
1026
 
897
1027
  def test_hidden(self):
898
1028
  module = "job_button_receiver"
@@ -903,7 +1033,7 @@ class JobButtonReceiverTest(TestCase):
903
1033
  def test_is_job_button(self):
904
1034
  with self.subTest(expected=False):
905
1035
  module = "pass"
906
- name = "TestPass"
1036
+ name = "TestPassJob"
907
1037
  _job_class, job_model = get_job_class_and_model(module, name)
908
1038
  self.assertFalse(job_model.is_job_button_receiver)
909
1039
 
@@ -955,7 +1085,7 @@ class JobHookReceiverTest(TestCase):
955
1085
  name = "TestJobHookReceiverLog"
956
1086
  job_class, _job_model = get_job_class_and_model(module, name)
957
1087
  form = job_class().as_form()
958
- self.assertSequenceEqual(list(form.fields.keys()), ["object_change", "_task_queue", "_profile"])
1088
+ self.assertSequenceEqual(list(form.fields.keys()), ["object_change", "_job_queue", "_profile"])
959
1089
 
960
1090
  def test_hidden(self):
961
1091
  module = "job_hook_receiver"
@@ -966,7 +1096,7 @@ class JobHookReceiverTest(TestCase):
966
1096
  def test_is_job_hook(self):
967
1097
  with self.subTest(expected=False):
968
1098
  module = "pass"
969
- name = "TestPass"
1099
+ name = "TestPassJob"
970
1100
  _job_class, job_model = get_job_class_and_model(module, name)
971
1101
  self.assertFalse(job_model.is_job_hook_receiver)
972
1102
 
@@ -1090,7 +1220,7 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
1090
1220
  ("info", "action: create"),
1091
1221
  ("info", f"jobresult.user: {self.user.username}"),
1092
1222
  ("info", "Test Job Hook Location 1"),
1093
- ("info", "Job completed"),
1223
+ ("success", "Job completed"),
1094
1224
  ]
1095
1225
  log_messages = models.JobLogEntry.objects.filter(job_result=job_result).values_list("log_level", "message")
1096
1226
  self.assertSequenceEqual(log_messages, expected_log_messages)
@@ -1116,7 +1246,7 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
1116
1246
  ("info", "action: update"),
1117
1247
  ("info", f"jobresult.user: {self.user.username}"),
1118
1248
  ("info", "Test Job Hook Location 1"),
1119
- ("info", "Job completed"),
1249
+ ("success", "Job completed"),
1120
1250
  ]
1121
1251
  log_messages = models.JobLogEntry.objects.filter(job_result=job_result).values_list("log_level", "message")
1122
1252
  self.assertSequenceEqual(log_messages, expected_log_messages)
@@ -1127,7 +1257,7 @@ class RemoveScheduledJobManagementCommandTestCase(TestCase):
1127
1257
  for i in range(1, 7):
1128
1258
  models.ScheduledJob.objects.create(
1129
1259
  name=f"test{i}",
1130
- task="pass.TestPass",
1260
+ task="pass.TestPassJob",
1131
1261
  interval=JobExecutionType.TYPE_FUTURE,
1132
1262
  user=self.user,
1133
1263
  start_time=timezone.now() - datetime.timedelta(days=i * 30),
@@ -1136,7 +1266,7 @@ class RemoveScheduledJobManagementCommandTestCase(TestCase):
1136
1266
 
1137
1267
  models.ScheduledJob.objects.create(
1138
1268
  name="test7",
1139
- task="pass.TestPass",
1269
+ task="pass.TestPassJob",
1140
1270
  interval=JobExecutionType.TYPE_DAILY,
1141
1271
  user=self.user,
1142
1272
  start_time=timezone.now() - datetime.timedelta(days=180),
@@ -1164,7 +1294,7 @@ class ScheduledJobIntervalTestCase(TestCase):
1164
1294
  start_time = timezone.now() + datetime.timedelta(days=6)
1165
1295
  scheduled_job = models.ScheduledJob.objects.create(
1166
1296
  name="weekly_interval",
1167
- task="pass.TestPass",
1297
+ task="pass.TestPassJob",
1168
1298
  interval=JobExecutionType.TYPE_WEEKLY,
1169
1299
  user=self.user,
1170
1300
  start_time=start_time,
@@ -4,6 +4,7 @@ import tempfile
4
4
  from unittest import expectedFailure, mock
5
5
  import uuid
6
6
  import warnings
7
+ from zoneinfo import ZoneInfo
7
8
 
8
9
  from django.conf import settings
9
10
  from django.contrib.auth import get_user_model
@@ -19,11 +20,6 @@ from django_celery_beat.tzcrontab import TzAwareCrontab
19
20
  from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
20
21
  import time_machine
21
22
 
22
- try:
23
- from zoneinfo import ZoneInfo
24
- except ImportError: # python 3.8
25
- from backports.zoneinfo import ZoneInfo
26
-
27
23
  from nautobot.circuits.models import CircuitType
28
24
  from nautobot.core.choices import ColorChoices
29
25
  from nautobot.core.testing import TestCase
@@ -66,6 +62,7 @@ from nautobot.extras.models import (
66
62
  GitRepository,
67
63
  Job as JobModel,
68
64
  JobLogEntry,
65
+ JobQueue,
69
66
  JobResult,
70
67
  MetadataChoice,
71
68
  MetadataType,
@@ -1086,7 +1083,7 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1086
1083
  @classmethod
1087
1084
  def setUpTestData(cls):
1088
1085
  # JobModel instances are automatically instantiated at startup, so we just need to look them up.
1089
- cls.local_job = JobModel.objects.get(job_class_name="TestPass")
1086
+ cls.local_job = JobModel.objects.get(job_class_name="TestPassJob")
1090
1087
  cls.job_containing_sensitive_variables = JobModel.objects.get(job_class_name="ExampleLoggingJob")
1091
1088
  cls.app_job = JobModel.objects.get(job_class_name="ExampleJob")
1092
1089
 
@@ -1098,7 +1095,7 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1098
1095
  self.assertEqual(self.app_job.job_class, ExampleJob)
1099
1096
 
1100
1097
  def test_class_path(self):
1101
- self.assertEqual(self.local_job.class_path, "pass.TestPass")
1098
+ self.assertEqual(self.local_job.class_path, "pass.TestPassJob")
1102
1099
  self.assertIsNotNone(self.local_job.job_class)
1103
1100
  self.assertEqual(self.local_job.class_path, self.local_job.job_class.class_path)
1104
1101
 
@@ -1135,6 +1132,11 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1135
1132
  getattr(job_model.job_class, field_name),
1136
1133
  field_name,
1137
1134
  )
1135
+ if not job_model.job_queues_override:
1136
+ self.assertEqual(
1137
+ sorted(job_model.task_queues),
1138
+ sorted(job_model.job_class.task_queues) or [settings.CELERY_TASK_DEFAULT_QUEUE],
1139
+ )
1138
1140
  except AssertionError:
1139
1141
  print(list(JobModel.objects.all()))
1140
1142
  print(registry["jobs"])
@@ -1159,7 +1161,6 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1159
1161
  "has_sensitive_variables": not self.job_containing_sensitive_variables.has_sensitive_variables,
1160
1162
  "soft_time_limit": 350,
1161
1163
  "time_limit": 650,
1162
- "task_queues": ["overridden", "worker", "queues"],
1163
1164
  }
1164
1165
 
1165
1166
  # Override values to non-defaults and ensure they are preserved
@@ -1237,6 +1238,25 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1237
1238
  "A job that may have sensitive variables cannot be marked as requiring approval",
1238
1239
  )
1239
1240
 
1241
+ def test_default_job_queue_always_included_in_job_queues(self):
1242
+ default_job_queue = JobQueue.objects.first()
1243
+ job_queues = list(JobQueue.objects.exclude(pk=default_job_queue.pk))[:3]
1244
+
1245
+ job = JobModel.objects.first()
1246
+ job.default_job_queue = default_job_queue
1247
+ job.save()
1248
+ job.job_queues.set(job_queues)
1249
+
1250
+ self.assertTrue(job.job_queues.filter(pk=default_job_queue.pk).exists())
1251
+
1252
+
1253
+ class JobQueueTest(ModelTestCases.BaseModelTestCase):
1254
+ """
1255
+ Tests for the `JobQueue` model class.
1256
+ """
1257
+
1258
+ model = JobQueue
1259
+
1240
1260
 
1241
1261
  class MetadataChoiceTest(ModelTestCases.BaseModelTestCase):
1242
1262
  model = MetadataChoice
@@ -1812,11 +1832,11 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
1812
1832
 
1813
1833
  def setUp(self):
1814
1834
  self.user = User.objects.create_user(username="scheduledjobuser")
1815
- self.job_model = JobModel.objects.get(name="TestPass")
1835
+ self.job_model = JobModel.objects.get(name="TestPassJob")
1816
1836
 
1817
1837
  self.daily_utc_job = ScheduledJob.objects.create(
1818
1838
  name="Daily UTC Job",
1819
- task="pass.TestPass",
1839
+ task="pass.TestPassJob",
1820
1840
  job_model=self.job_model,
1821
1841
  interval=JobExecutionType.TYPE_DAILY,
1822
1842
  start_time=datetime(year=2050, month=1, day=22, hour=17, minute=0, tzinfo=get_default_timezone()),
@@ -1824,7 +1844,7 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
1824
1844
  )
1825
1845
  self.daily_est_job = ScheduledJob.objects.create(
1826
1846
  name="Daily EST Job",
1827
- task="pass.TestPass",
1847
+ task="pass.TestPassJob",
1828
1848
  job_model=self.job_model,
1829
1849
  interval=JobExecutionType.TYPE_DAILY,
1830
1850
  start_time=datetime(year=2050, month=1, day=22, hour=17, minute=0, tzinfo=ZoneInfo("America/New_York")),
@@ -1839,7 +1859,7 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
1839
1859
  )
1840
1860
  self.crontab_est_job = ScheduledJob.objects.create(
1841
1861
  name="Crontab EST Job",
1842
- task="pass.TestPass",
1862
+ task="pass.TestPassJob",
1843
1863
  job_model=self.job_model,
1844
1864
  interval=JobExecutionType.TYPE_CUSTOM,
1845
1865
  start_time=datetime(year=2050, month=1, day=22, hour=17, minute=0, tzinfo=ZoneInfo("America/New_York")),
@@ -1848,7 +1868,7 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
1848
1868
  )
1849
1869
  self.one_off_utc_job = ScheduledJob.objects.create(
1850
1870
  name="One-off UTC Job",
1851
- task="pass.TestPass",
1871
+ task="pass.TestPassJob",
1852
1872
  job_model=self.job_model,
1853
1873
  interval=JobExecutionType.TYPE_FUTURE,
1854
1874
  start_time=datetime(year=2050, month=1, day=22, hour=0, minute=0, tzinfo=ZoneInfo("UTC")),
@@ -1862,6 +1882,14 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
1862
1882
  start_time=datetime(year=2050, month=1, day=22, hour=0, minute=0, tzinfo=ZoneInfo("America/New_York")),
1863
1883
  )
1864
1884
 
1885
+ def test_scheduled_job_queue_setter(self):
1886
+ """Test the queue property setter on ScheduledJob."""
1887
+ invalid_queue = "Invalid job Queue"
1888
+ with self.assertRaises(ValidationError) as cm:
1889
+ self.daily_utc_job.queue = invalid_queue
1890
+ self.daily_utc_job.validated_save()
1891
+ self.assertIn(f"Job Queue {invalid_queue} does not exist in the database.", str(cm.exception))
1892
+
1865
1893
  def test_schedule(self):
1866
1894
  """Test the schedule property."""
1867
1895
  with self.subTest("Test TYPE_DAILY schedules"):
@@ -1987,7 +2015,7 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
1987
2015
  """Test that TYPE_CUSTOM behavior around DST is as expected."""
1988
2016
  cronjob = ScheduledJob.objects.create(
1989
2017
  name="DST Aware Cronjob",
1990
- task="pass.TestPass",
2018
+ task="pass.TestPassJob",
1991
2019
  job_model=self.job_model,
1992
2020
  enabled=False,
1993
2021
  interval=JobExecutionType.TYPE_CUSTOM,
@@ -2042,7 +2070,7 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
2042
2070
  """Test the interaction of TYPE_DAILY around DST."""
2043
2071
  daily = ScheduledJob.objects.create(
2044
2072
  name="Daily Job",
2045
- task="pass.TestPass",
2073
+ task="pass.TestPassJob",
2046
2074
  job_model=self.job_model,
2047
2075
  enabled=False,
2048
2076
  interval=JobExecutionType.TYPE_DAILY,
@@ -2092,6 +2120,33 @@ class ScheduledJobTest(ModelTestCases.BaseModelTestCase):
2092
2120
  is_due, _ = crontab.is_due(last_run_at=datetime(2024, 3, 9, 17, 0, tzinfo=ZoneInfo("America/New_York")))
2093
2121
  self.assertTrue(is_due)
2094
2122
 
2123
+ # TODO uncomment when we have a way to setup the NautobotDatabaseScheduler correctly
2124
+ # @mock.patch("nautobot.extras.utils.run_kubernetes_job_and_return_job_result")
2125
+ # def test_nautobot_database_scheduler_apply_async_method(self, mock_run_kubernetes_job_and_return_job_result):
2126
+ # jq = JobQueue.objects.create(name="kubernetes", queue_type=JobQueueTypeChoices.TYPE_KUBERNETES)
2127
+ # sj = ScheduledJob.objects.create(
2128
+ # name="Export Object List Hourly",
2129
+ # task="nautobot.core.jobs.ExportObjectList",
2130
+ # job_model=JobModel.objects.get(name="Export Object List"),
2131
+ # interval=JobExecutionType.TYPE_HOURLY,
2132
+ # user=User.objects.first(),
2133
+ # approval_required=False,
2134
+ # start_time=datetime.now(ZoneInfo("America/New_York")),
2135
+ # time_zone=ZoneInfo("America/New_York"),
2136
+ # job_queue=jq,
2137
+ # kwargs='{"content_type": 1}',
2138
+ # )
2139
+ # jr = JobResult.objects.create(
2140
+ # name=sj.job_model.name,
2141
+ # job_model=sj.job_model,
2142
+ # scheduled_job=sj,
2143
+ # user=sj.user,
2144
+ # )
2145
+ # mock_run_kubernetes_job_and_return_job_result.return_value = jr
2146
+ # entry = NautobotScheduleEntry(model=sj)
2147
+ # scheduler = NautobotDatabaseScheduler(app=entry.app)
2148
+ # scheduler.apply_async(entry=entry, producer=None, advance=False)
2149
+
2095
2150
 
2096
2151
  class SecretTest(ModelTestCases.BaseModelTestCase):
2097
2152
  """
@@ -2583,7 +2638,7 @@ class JobLogEntryTest(TestCase): # TODO: change to BaseModelTestCase
2583
2638
 
2584
2639
  def setUp(self):
2585
2640
  module = "pass"
2586
- name = "TestPass"
2641
+ name = "TestPassJob"
2587
2642
  job_class = get_job(f"{module}.{name}")
2588
2643
 
2589
2644
  self.job_result = JobResult.objects.create(name=job_class.class_path, user=None)