nautobot 2.3.16__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 (721) 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 +140 -23
  32. nautobot/cloud/api/views.py +6 -10
  33. nautobot/cloud/factory.py +4 -1
  34. nautobot/cloud/tests/test_filters.py +5 -4
  35. nautobot/cloud/views.py +0 -16
  36. nautobot/core/api/constants.py +11 -0
  37. nautobot/core/api/filter_backends.py +3 -9
  38. nautobot/core/api/metadata.py +28 -256
  39. nautobot/core/api/pagination.py +3 -2
  40. nautobot/core/api/renderers.py +3 -0
  41. nautobot/core/api/schema.py +13 -2
  42. nautobot/core/api/serializers.py +45 -259
  43. nautobot/core/api/urls.py +3 -4
  44. nautobot/core/api/utils.py +0 -62
  45. nautobot/core/api/views.py +99 -157
  46. nautobot/core/apps/__init__.py +22 -578
  47. nautobot/core/celery/__init__.py +13 -0
  48. nautobot/core/celery/schedulers.py +47 -2
  49. nautobot/core/choices.py +2 -2
  50. nautobot/core/cli/__init__.py +8 -0
  51. nautobot/core/constants.py +7 -0
  52. nautobot/core/events/__init__.py +116 -0
  53. nautobot/core/events/base.py +27 -0
  54. nautobot/core/events/exceptions.py +10 -0
  55. nautobot/core/events/redis_broker.py +48 -0
  56. nautobot/core/events/syslog_broker.py +19 -0
  57. nautobot/core/exceptions.py +0 -6
  58. nautobot/core/forms/__init__.py +19 -19
  59. nautobot/core/forms/fields.py +57 -9
  60. nautobot/core/forms/forms.py +33 -2
  61. nautobot/core/forms/utils.py +2 -1
  62. nautobot/core/graphql/schema.py +3 -1
  63. nautobot/core/jobs/__init__.py +24 -3
  64. nautobot/core/jobs/bulk_actions.py +248 -0
  65. nautobot/core/jobs/cleanup.py +1 -1
  66. nautobot/core/management/commands/generate_test_data.py +21 -0
  67. nautobot/core/middleware.py +16 -0
  68. nautobot/core/models/fields.py +11 -7
  69. nautobot/core/settings.py +68 -4
  70. nautobot/core/settings.yaml +99 -0
  71. nautobot/core/tables.py +10 -46
  72. nautobot/core/tasks.py +1 -1
  73. nautobot/core/templates/about.html +67 -0
  74. nautobot/core/templates/components/button/default.html +7 -0
  75. nautobot/core/templates/components/button/dropdown.html +20 -0
  76. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  77. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  78. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  79. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  80. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  81. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  82. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  83. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  84. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  85. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  86. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  87. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  88. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  89. nautobot/core/templates/components/panel/panel.html +16 -0
  90. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  91. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  92. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  93. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  94. nautobot/core/templates/generic/object_retrieve.html +28 -17
  95. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  96. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  97. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  98. nautobot/core/templates/inc/footer.html +1 -0
  99. nautobot/core/templates/inc/nav_menu.html +2 -1
  100. nautobot/core/templates/inc/relationships_panel.html +1 -1
  101. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  102. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  103. nautobot/core/templates/nautobot_config.py.j2 +13 -0
  104. nautobot/core/templates/panel_table.html +12 -0
  105. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  106. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  107. nautobot/core/templates/utilities/theme_preview.html +7 -0
  108. nautobot/core/templatetags/helpers.py +104 -6
  109. nautobot/core/templatetags/ui_framework.py +40 -0
  110. nautobot/core/testing/__init__.py +8 -8
  111. nautobot/core/testing/api.py +187 -137
  112. nautobot/core/testing/context.py +18 -0
  113. nautobot/core/testing/filters.py +41 -35
  114. nautobot/core/testing/forms.py +2 -0
  115. nautobot/core/testing/views.py +65 -148
  116. nautobot/core/tests/integration/test_view_authentication.py +1 -1
  117. nautobot/core/tests/nautobot_config.py +198 -0
  118. nautobot/core/tests/runner.py +2 -2
  119. nautobot/core/tests/test_api.py +154 -176
  120. nautobot/core/tests/test_events.py +214 -0
  121. nautobot/core/tests/test_forms.py +1 -0
  122. nautobot/core/tests/test_jinja_filters.py +1 -0
  123. nautobot/core/tests/test_jobs.py +387 -14
  124. nautobot/core/tests/test_navigations.py +7 -241
  125. nautobot/core/tests/test_settings_schema.py +7 -0
  126. nautobot/core/tests/test_tables.py +100 -0
  127. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  128. nautobot/core/tests/test_ui.py +150 -0
  129. nautobot/core/tests/test_utils.py +55 -18
  130. nautobot/core/tests/test_views.py +124 -5
  131. nautobot/core/ui/__init__.py +0 -0
  132. nautobot/core/ui/base.py +11 -0
  133. nautobot/core/ui/choices.py +44 -0
  134. nautobot/core/ui/homepage.py +167 -0
  135. nautobot/core/ui/nav.py +280 -0
  136. nautobot/core/ui/object_detail.py +1855 -0
  137. nautobot/core/ui/utils.py +36 -0
  138. nautobot/core/urls.py +6 -0
  139. nautobot/core/utils/config.py +30 -3
  140. nautobot/core/utils/lookup.py +12 -2
  141. nautobot/core/utils/querysets.py +64 -0
  142. nautobot/core/utils/requests.py +24 -9
  143. nautobot/core/views/__init__.py +48 -1
  144. nautobot/core/views/generic.py +37 -140
  145. nautobot/core/views/mixins.py +82 -32
  146. nautobot/core/views/paginator.py +8 -5
  147. nautobot/core/views/renderers.py +9 -9
  148. nautobot/core/views/utils.py +11 -0
  149. nautobot/core/wsgi.py +3 -3
  150. nautobot/dcim/api/serializers.py +34 -141
  151. nautobot/dcim/api/urls.py +5 -0
  152. nautobot/dcim/api/views.py +57 -110
  153. nautobot/dcim/apps.py +1 -0
  154. nautobot/dcim/choices.py +28 -0
  155. nautobot/dcim/factory.py +58 -0
  156. nautobot/dcim/filters/__init__.py +204 -2
  157. nautobot/dcim/forms.py +219 -9
  158. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  159. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  160. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  161. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  162. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  163. nautobot/dcim/models/__init__.py +5 -1
  164. nautobot/dcim/models/devices.py +180 -2
  165. nautobot/dcim/models/racks.py +2 -2
  166. nautobot/dcim/navigation.py +25 -224
  167. nautobot/dcim/signals.py +44 -0
  168. nautobot/dcim/tables/__init__.py +2 -0
  169. nautobot/dcim/tables/devices.py +103 -7
  170. nautobot/dcim/tables/racks.py +1 -1
  171. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  172. nautobot/dcim/templates/dcim/controller_create.html +2 -7
  173. nautobot/dcim/templates/dcim/controller_retrieve.html +6 -10
  174. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  175. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -0
  176. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +51 -0
  177. nautobot/dcim/templates/dcim/device/base.html +6 -42
  178. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  179. nautobot/dcim/templates/dcim/device.html +4 -10
  180. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  181. nautobot/dcim/templates/dcim/interface.html +1 -0
  182. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  183. nautobot/dcim/templates/dcim/location.html +1 -9
  184. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  185. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  186. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  187. nautobot/dcim/templates/dcim/rack.html +1 -9
  188. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  189. nautobot/dcim/templates/dcim/rackreservation.html +1 -9
  190. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +68 -0
  191. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +28 -0
  192. nautobot/dcim/tests/integration/test_controller.py +62 -0
  193. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  194. nautobot/dcim/tests/test_api.py +188 -64
  195. nautobot/dcim/tests/test_filters.py +171 -76
  196. nautobot/dcim/tests/test_jobs.py +118 -0
  197. nautobot/dcim/tests/test_models.py +157 -6
  198. nautobot/dcim/tests/test_signals.py +1 -0
  199. nautobot/dcim/tests/test_views.py +118 -88
  200. nautobot/dcim/urls.py +72 -27
  201. nautobot/dcim/utils.py +2 -2
  202. nautobot/dcim/views.py +356 -61
  203. nautobot/extras/api/serializers.py +39 -18
  204. nautobot/extras/api/urls.py +4 -0
  205. nautobot/extras/api/views.py +89 -31
  206. nautobot/extras/choices.py +13 -0
  207. nautobot/extras/constants.py +2 -1
  208. nautobot/extras/context_managers.py +23 -6
  209. nautobot/extras/datasources/git.py +4 -1
  210. nautobot/extras/factory.py +27 -0
  211. nautobot/extras/filters/__init__.py +66 -5
  212. nautobot/extras/forms/base.py +2 -2
  213. nautobot/extras/forms/forms.py +262 -59
  214. nautobot/extras/forms/mixins.py +2 -2
  215. nautobot/extras/graphql/types.py +25 -1
  216. nautobot/extras/jobs.py +109 -15
  217. nautobot/extras/management/__init__.py +1 -0
  218. nautobot/extras/management/commands/runjob.py +7 -79
  219. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  220. nautobot/extras/management/utils.py +87 -0
  221. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  222. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  223. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  224. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  225. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  226. nautobot/extras/models/__init__.py +5 -1
  227. nautobot/extras/models/change_logging.py +7 -3
  228. nautobot/extras/models/contacts.py +1 -1
  229. nautobot/extras/models/groups.py +0 -2
  230. nautobot/extras/models/jobs.py +233 -33
  231. nautobot/extras/models/relationships.py +69 -1
  232. nautobot/extras/models/secrets.py +5 -0
  233. nautobot/extras/navigation.py +20 -262
  234. nautobot/extras/plugins/__init__.py +54 -19
  235. nautobot/extras/plugins/marketplace_manifest.yml +455 -0
  236. nautobot/extras/plugins/tables.py +16 -14
  237. nautobot/extras/plugins/urls.py +1 -0
  238. nautobot/extras/plugins/views.py +103 -60
  239. nautobot/extras/registry.py +1 -1
  240. nautobot/extras/secrets/__init__.py +2 -2
  241. nautobot/extras/signals.py +39 -1
  242. nautobot/extras/tables.py +37 -1
  243. nautobot/extras/templates/extras/dynamicgroup.html +1 -9
  244. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  245. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  246. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  247. nautobot/extras/templates/extras/job_detail.html +52 -6
  248. nautobot/extras/templates/extras/job_edit.html +6 -2
  249. nautobot/extras/templates/extras/job_list.html +2 -7
  250. nautobot/extras/templates/extras/jobqueue_retrieve.html +36 -0
  251. nautobot/extras/templates/extras/marketplace.html +296 -0
  252. nautobot/extras/templates/extras/plugin_detail.html +32 -15
  253. nautobot/extras/templates/extras/plugins_list.html +35 -1
  254. nautobot/extras/templates/extras/plugins_tiles.html +90 -0
  255. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  256. nautobot/extras/templates/extras/secret.html +0 -65
  257. nautobot/extras/templates/extras/secret_check.js +16 -0
  258. nautobot/extras/templates/extras/secret_create.html +114 -0
  259. nautobot/extras/templates/extras/secret_edit.html +1 -114
  260. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  261. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  262. nautobot/extras/templatetags/job_buttons.py +5 -4
  263. nautobot/extras/templatetags/plugins.py +69 -6
  264. nautobot/extras/test_jobs/singleton.py +16 -0
  265. nautobot/extras/tests/test_api.py +145 -43
  266. nautobot/extras/tests/test_context_managers.py +4 -1
  267. nautobot/extras/tests/test_filters.py +213 -529
  268. nautobot/extras/tests/test_job_variables.py +73 -152
  269. nautobot/extras/tests/test_jobs.py +181 -51
  270. nautobot/extras/tests/test_models.py +61 -6
  271. nautobot/extras/tests/test_plugins.py +62 -9
  272. nautobot/extras/tests/test_relationships.py +123 -9
  273. nautobot/extras/tests/test_utils.py +23 -2
  274. nautobot/extras/tests/test_views.py +146 -145
  275. nautobot/extras/tests/test_webhooks.py +2 -1
  276. nautobot/extras/urls.py +2 -20
  277. nautobot/extras/utils.py +119 -4
  278. nautobot/extras/views.py +168 -125
  279. nautobot/extras/webhooks.py +5 -2
  280. nautobot/ipam/api/serializers.py +10 -103
  281. nautobot/ipam/api/views.py +31 -49
  282. nautobot/ipam/factory.py +1 -1
  283. nautobot/ipam/filters.py +3 -2
  284. nautobot/ipam/models.py +10 -12
  285. nautobot/ipam/navigation.py +0 -90
  286. nautobot/ipam/tables.py +3 -1
  287. nautobot/ipam/templates/ipam/ipaddress.html +1 -9
  288. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  289. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  290. nautobot/ipam/templates/ipam/prefix.html +1 -9
  291. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  292. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  293. nautobot/ipam/templates/ipam/vlan.html +1 -9
  294. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  295. nautobot/ipam/templates/ipam/vrf.html +0 -47
  296. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  297. nautobot/ipam/tests/test_api.py +7 -5
  298. nautobot/ipam/tests/test_filters.py +39 -119
  299. nautobot/ipam/tests/test_forms.py +0 -2
  300. nautobot/ipam/tests/test_models.py +56 -36
  301. nautobot/ipam/tests/test_views.py +3 -0
  302. nautobot/ipam/urls.py +3 -69
  303. nautobot/ipam/utils/__init__.py +16 -10
  304. nautobot/ipam/views.py +91 -162
  305. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  306. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  307. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  308. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  309. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  310. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  311. nautobot/project-static/css/base.css +38 -3
  312. nautobot/project-static/docs/404.html +461 -17
  313. nautobot/project-static/docs/apps/index.html +461 -17
  314. nautobot/project-static/docs/apps/nautobot-apps.html +462 -19
  315. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  316. nautobot/project-static/docs/assets/extra.css +5 -1
  317. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +477 -23
  318. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +474 -20
  319. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +790 -289
  320. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +505 -31
  321. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +510 -34
  322. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +471 -20
  323. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +467 -18
  324. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +497 -33
  325. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9883 -0
  326. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +523 -75
  327. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +546 -51
  328. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +670 -94
  329. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1030 -177
  330. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +524 -49
  331. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +874 -188
  332. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +955 -235
  333. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +475 -21
  334. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +486 -28
  335. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +661 -99
  336. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +947 -479
  337. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6425 -1234
  338. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +474 -20
  339. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +877 -344
  340. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +828 -171
  341. nautobot/project-static/docs/development/apps/api/configuration-view.html +461 -17
  342. nautobot/project-static/docs/development/apps/api/database-backend-config.html +461 -17
  343. nautobot/project-static/docs/development/apps/api/models/django-admin.html +461 -17
  344. nautobot/project-static/docs/development/apps/api/models/global-search.html +461 -17
  345. nautobot/project-static/docs/development/apps/api/models/graphql.html +461 -17
  346. nautobot/project-static/docs/development/apps/api/models/index.html +461 -17
  347. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +461 -17
  348. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +461 -17
  349. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +461 -17
  350. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +461 -17
  351. nautobot/project-static/docs/development/apps/api/platform-features/index.html +461 -17
  352. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +461 -17
  353. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +461 -17
  354. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +461 -17
  355. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +461 -17
  356. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +461 -17
  357. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +461 -17
  358. nautobot/project-static/docs/development/apps/api/prometheus.html +461 -17
  359. nautobot/project-static/docs/development/apps/api/setup.html +465 -153
  360. nautobot/project-static/docs/development/apps/api/testing.html +461 -17
  361. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +461 -17
  362. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +461 -17
  363. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +461 -17
  364. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +461 -17
  365. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +741 -128
  366. nautobot/project-static/docs/development/apps/api/views/base-template.html +461 -17
  367. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +461 -17
  368. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +461 -17
  369. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +461 -17
  370. nautobot/project-static/docs/development/apps/api/views/index.html +463 -18
  371. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +465 -17
  372. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +491 -17
  373. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +461 -17
  374. nautobot/project-static/docs/development/apps/api/views/notes.html +461 -17
  375. nautobot/project-static/docs/development/apps/api/views/rest-api.html +467 -19
  376. nautobot/project-static/docs/development/apps/api/views/urls.html +461 -17
  377. nautobot/project-static/docs/development/apps/index.html +461 -17
  378. nautobot/project-static/docs/development/apps/migration/code-updates.html +462 -50
  379. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +462 -18
  380. nautobot/project-static/docs/development/apps/migration/from-v1.html +461 -17
  381. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +461 -17
  382. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +461 -17
  383. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +461 -17
  384. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +464 -20
  385. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9261 -0
  386. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9375 -0
  387. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9671 -0
  388. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9559 -0
  389. nautobot/project-static/docs/development/apps/porting-from-netbox.html +464 -20
  390. nautobot/project-static/docs/development/core/application-registry.html +461 -17
  391. nautobot/project-static/docs/development/core/best-practices.html +461 -17
  392. nautobot/project-static/docs/development/core/bootstrap-ui.html +461 -17
  393. nautobot/project-static/docs/development/core/caching.html +461 -17
  394. nautobot/project-static/docs/development/core/controllers.html +463 -17
  395. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +464 -20
  396. nautobot/project-static/docs/development/core/generic-views.html +461 -17
  397. nautobot/project-static/docs/development/core/getting-started.html +539 -127
  398. nautobot/project-static/docs/development/core/homepage.html +472 -28
  399. nautobot/project-static/docs/development/core/index.html +461 -17
  400. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9754 -0
  401. nautobot/project-static/docs/development/core/model-checklist.html +471 -25
  402. nautobot/project-static/docs/development/core/model-features.html +461 -17
  403. nautobot/project-static/docs/development/core/natural-keys.html +461 -17
  404. nautobot/project-static/docs/development/core/navigation-menu.html +478 -24
  405. nautobot/project-static/docs/development/core/release-checklist.html +478 -46
  406. nautobot/project-static/docs/development/core/role-internals.html +461 -17
  407. nautobot/project-static/docs/development/core/settings.html +461 -17
  408. nautobot/project-static/docs/development/core/style-guide.html +464 -20
  409. nautobot/project-static/docs/development/core/templates.html +471 -20
  410. nautobot/project-static/docs/development/core/testing.html +461 -17
  411. nautobot/project-static/docs/development/core/ui-component-framework.html +11116 -0
  412. nautobot/project-static/docs/development/core/user-preferences.html +464 -20
  413. nautobot/project-static/docs/development/index.html +461 -17
  414. nautobot/project-static/docs/development/jobs/index.html +499 -19
  415. nautobot/project-static/docs/development/jobs/migration/from-v1.html +461 -17
  416. nautobot/project-static/docs/index.html +469 -36
  417. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  418. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  419. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  420. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  421. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  422. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  423. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  424. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  425. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  426. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  427. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  428. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  429. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  430. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  431. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  432. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  433. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  434. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  435. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  436. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  437. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  438. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  439. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  440. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  441. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  442. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  443. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  444. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  445. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  446. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  447. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  448. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  449. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  450. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  451. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  452. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  453. nautobot/project-static/docs/objects.inv +0 -0
  454. nautobot/project-static/docs/overview/application_stack.html +467 -21
  455. nautobot/project-static/docs/overview/design_philosophy.html +461 -17
  456. nautobot/project-static/docs/release-notes/index.html +483 -20
  457. nautobot/project-static/docs/release-notes/version-1.0.html +649 -206
  458. nautobot/project-static/docs/release-notes/version-1.1.html +646 -203
  459. nautobot/project-static/docs/release-notes/version-1.2.html +721 -278
  460. nautobot/project-static/docs/release-notes/version-1.3.html +747 -304
  461. nautobot/project-static/docs/release-notes/version-1.4.html +832 -390
  462. nautobot/project-static/docs/release-notes/version-1.5.html +1020 -579
  463. nautobot/project-static/docs/release-notes/version-1.6.html +940 -516
  464. nautobot/project-static/docs/release-notes/version-2.0.html +943 -502
  465. nautobot/project-static/docs/release-notes/version-2.1.html +778 -337
  466. nautobot/project-static/docs/release-notes/version-2.2.html +771 -330
  467. nautobot/project-static/docs/release-notes/version-2.3.html +914 -471
  468. nautobot/project-static/docs/release-notes/version-2.4.html +10323 -0
  469. nautobot/project-static/docs/search/search_index.json +1 -1
  470. nautobot/project-static/docs/sitemap.xml +342 -270
  471. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  472. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +461 -17
  473. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +461 -17
  474. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +461 -17
  475. nautobot/project-static/docs/user-guide/administration/configuration/index.html +473 -30
  476. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +461 -17
  477. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +842 -155
  478. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +461 -17
  479. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +461 -17
  480. nautobot/project-static/docs/user-guide/administration/guides/docker.html +474 -27
  481. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +461 -17
  482. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +461 -17
  483. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +461 -17
  484. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +461 -17
  485. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +461 -17
  486. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +463 -19
  487. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +461 -17
  488. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +461 -17
  489. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +461 -17
  490. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +481 -21
  491. nautobot/project-static/docs/user-guide/administration/installation/index.html +466 -18
  492. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +462 -18
  493. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +461 -17
  494. nautobot/project-static/docs/user-guide/administration/installation/services.html +461 -17
  495. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +461 -17
  496. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +482 -39
  497. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +475 -64
  498. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +475 -64
  499. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +461 -17
  500. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +461 -17
  501. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +461 -17
  502. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +461 -17
  503. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +461 -17
  504. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +464 -21
  505. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +461 -17
  506. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  507. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +461 -17
  508. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +467 -19
  509. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +461 -17
  510. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +461 -17
  511. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +461 -17
  512. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +461 -17
  513. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +461 -17
  514. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +461 -17
  515. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +461 -17
  516. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +461 -17
  517. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +461 -17
  518. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +461 -17
  519. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +461 -17
  520. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +461 -17
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +461 -17
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +461 -17
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +461 -17
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +461 -17
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +461 -17
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +497 -18
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +487 -20
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +461 -17
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +461 -17
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +461 -17
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +461 -17
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +461 -17
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +461 -17
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +461 -17
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +461 -17
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +461 -17
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +461 -17
  538. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +461 -17
  539. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +461 -17
  540. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +461 -17
  541. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +461 -17
  542. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +461 -17
  543. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +461 -17
  544. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +461 -17
  545. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +461 -17
  546. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +461 -17
  547. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +461 -17
  548. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +461 -17
  549. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +461 -17
  550. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +461 -17
  551. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +461 -17
  552. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +461 -17
  553. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +461 -17
  554. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +461 -17
  555. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +461 -17
  556. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +461 -17
  557. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +461 -17
  558. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +461 -17
  559. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +461 -17
  560. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +461 -17
  561. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +461 -17
  562. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9375 -0
  563. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +468 -28
  564. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +461 -17
  565. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +461 -17
  566. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +461 -17
  567. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +461 -17
  568. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +461 -17
  569. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +461 -17
  570. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +461 -17
  571. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +461 -17
  572. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +461 -17
  573. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +461 -17
  574. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +461 -17
  575. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +461 -17
  576. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +464 -20
  577. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +461 -17
  578. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +461 -17
  579. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +461 -17
  580. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +461 -17
  581. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +461 -17
  582. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +461 -17
  583. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +464 -20
  584. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9313 -0
  585. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9217 -0
  586. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9211 -0
  587. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9277 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +461 -17
  589. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +461 -17
  590. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +461 -17
  591. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +461 -17
  592. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +461 -17
  593. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +461 -17
  594. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +461 -17
  595. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +461 -17
  596. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +461 -17
  597. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +461 -17
  598. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +461 -17
  599. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +466 -20
  600. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +461 -17
  601. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  602. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  603. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  604. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  605. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  606. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  607. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  608. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  609. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  610. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  611. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  612. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  613. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  614. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +461 -17
  615. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +461 -17
  616. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +464 -20
  617. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
  618. nautobot/project-static/docs/user-guide/index.html +461 -17
  619. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +464 -20
  620. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +465 -21
  621. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +461 -17
  622. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +461 -17
  623. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +461 -17
  624. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9617 -0
  625. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +464 -20
  626. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +461 -17
  627. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +461 -17
  628. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +461 -17
  629. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +461 -17
  630. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +461 -17
  631. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +470 -21
  632. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +464 -20
  633. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +464 -20
  634. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +461 -17
  635. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9224 -0
  636. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
  637. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +464 -20
  638. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +461 -17
  639. nautobot/project-static/docs/user-guide/platform-functionality/note.html +461 -17
  640. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +461 -17
  641. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +465 -21
  642. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9292 -0
  643. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +461 -17
  644. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +509 -38
  645. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +492 -21
  646. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +461 -17
  647. nautobot/project-static/docs/user-guide/platform-functionality/role.html +461 -17
  648. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +461 -17
  649. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +461 -17
  650. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +464 -20
  651. nautobot/project-static/docs/user-guide/platform-functionality/status.html +461 -17
  652. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +461 -17
  653. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +529 -54
  654. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +461 -17
  655. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +461 -17
  656. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +461 -17
  657. nautobot/project-static/img/jinja_logo.svg +97 -0
  658. nautobot/project-static/js/forms.js +6 -1
  659. nautobot/project-static/js/nav_menu.js +2 -1
  660. nautobot/tenancy/api/serializers.py +0 -2
  661. nautobot/tenancy/api/views.py +9 -13
  662. nautobot/tenancy/factory.py +1 -1
  663. nautobot/tenancy/navigation.py +0 -29
  664. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  665. nautobot/tenancy/tests/test_filters.py +29 -134
  666. nautobot/tenancy/views.py +35 -24
  667. nautobot/users/admin.py +2 -0
  668. nautobot/users/api/views.py +2 -2
  669. nautobot/users/forms.py +19 -0
  670. nautobot/users/templates/users/preferences.html +22 -0
  671. nautobot/users/tests/test_filters.py +1 -19
  672. nautobot/users/tests/test_views.py +57 -0
  673. nautobot/users/utils.py +8 -0
  674. nautobot/users/views.py +48 -11
  675. nautobot/virtualization/api/views.py +5 -24
  676. nautobot/virtualization/filters.py +1 -2
  677. nautobot/virtualization/models.py +1 -1
  678. nautobot/virtualization/navigation.py +0 -48
  679. nautobot/virtualization/tables.py +2 -2
  680. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  681. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  682. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
  683. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  684. nautobot/virtualization/tests/test_filters.py +57 -166
  685. nautobot/virtualization/views.py +18 -15
  686. nautobot/wireless/__init__.py +0 -0
  687. nautobot/wireless/api/__init__.py +0 -0
  688. nautobot/wireless/api/serializers.py +44 -0
  689. nautobot/wireless/api/urls.py +20 -0
  690. nautobot/wireless/api/views.py +34 -0
  691. nautobot/wireless/apps.py +8 -0
  692. nautobot/wireless/choices.py +345 -0
  693. nautobot/wireless/factory.py +138 -0
  694. nautobot/wireless/filters.py +167 -0
  695. nautobot/wireless/forms.py +283 -0
  696. nautobot/wireless/homepage.py +19 -0
  697. nautobot/wireless/migrations/0001_initial.py +223 -0
  698. nautobot/wireless/migrations/__init__.py +0 -0
  699. nautobot/wireless/models.py +207 -0
  700. nautobot/wireless/navigation.py +105 -0
  701. nautobot/wireless/tables.py +244 -0
  702. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  703. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  704. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  705. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +56 -0
  706. nautobot/wireless/tests/__init__.py +0 -0
  707. nautobot/wireless/tests/integration/__init__.py +0 -0
  708. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  709. nautobot/wireless/tests/test_api.py +247 -0
  710. nautobot/wireless/tests/test_filters.py +82 -0
  711. nautobot/wireless/tests/test_models.py +22 -0
  712. nautobot/wireless/tests/test_views.py +378 -0
  713. nautobot/wireless/urls.py +13 -0
  714. nautobot/wireless/views.py +119 -0
  715. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/METADATA +9 -12
  716. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/RECORD +720 -549
  717. nautobot/core/utils/navigation.py +0 -54
  718. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/LICENSE.txt +0 -0
  719. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/NOTICE +0 -0
  720. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/WHEEL +0 -0
  721. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,78 @@
1
+ # Generated by Django 4.2.15 on 2024-08-20 14:58
2
+
3
+ from django.db import migrations
4
+
5
+ from nautobot.extras.choices import JobQueueTypeChoices
6
+
7
+
8
+ def migrate_task_queues_to_job_queues(apps, schema):
9
+ # Get the relevant models
10
+ Job = apps.get_model("extras", "Job")
11
+ ScheduledJob = apps.get_model("extras", "ScheduledJob")
12
+ JobQueue = apps.get_model("extras", "JobQueue")
13
+
14
+ # should always create default job queue
15
+ JobQueue.objects.get_or_create(name="default", defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY})
16
+ for job in Job.objects.all():
17
+ task_queues = job.task_queues
18
+ if len(task_queues) > 0:
19
+ default_job_queue, _ = JobQueue.objects.get_or_create(
20
+ name=task_queues[0], defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
21
+ )
22
+ else:
23
+ default_job_queue = JobQueue.objects.get(name="default")
24
+ job.default_job_queue = default_job_queue
25
+ job.save()
26
+ # Go through each task_queue
27
+ # make or get the corresponding job_queue object and assign it to the job
28
+ job_queues = [job.default_job_queue]
29
+ for task_queue in task_queues[1:]:
30
+ job_queue, _ = JobQueue.objects.get_or_create(
31
+ name=task_queue, defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
32
+ )
33
+ job_queues.append(job_queue)
34
+ job.job_queues.set(job_queues)
35
+
36
+ for sj in ScheduledJob.objects.all():
37
+ queue = sj.queue
38
+ job_queue = None
39
+ if queue:
40
+ job_queue, _ = JobQueue.objects.get_or_create(
41
+ name=queue, defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
42
+ )
43
+ sj.job_queue = job_queue
44
+ sj.save()
45
+
46
+
47
+ def reverse_migrate_task_queues_to_job_queues(apps, schema):
48
+ Job = apps.get_model("extras", "Job")
49
+ ScheduledJob = apps.get_model("extras", "ScheduledJob")
50
+ JobQueueAssignment = apps.get_model("extras", "JobQueueAssignment")
51
+
52
+ for job in Job.objects.all():
53
+ queue_names = list(job.job_queues.all().values_list("name", flat=True))
54
+ job.task_queues = queue_names
55
+ job.save()
56
+ JobQueueAssignment.objects.all().delete()
57
+
58
+ for sj in ScheduledJob.objects.all():
59
+ job_queue = sj.job_queue
60
+ if job_queue:
61
+ sj.queue = job_queue.name
62
+ else:
63
+ sj.queue = ""
64
+ sj.job_queue = None
65
+ sj.save()
66
+
67
+
68
+ class Migration(migrations.Migration):
69
+ dependencies = [
70
+ ("extras", "0117_create_job_queue_model"),
71
+ ]
72
+
73
+ operations = [
74
+ migrations.RunPython(
75
+ code=migrate_task_queues_to_job_queues,
76
+ reverse_code=reverse_migrate_task_queues_to_job_queues,
77
+ )
78
+ ]
@@ -0,0 +1,28 @@
1
+ # Generated by Django 4.2.15 on 2024-08-28 15:49
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ("extras", "0118_task_queue_to_job_queue_migration"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name="job",
15
+ name="task_queues",
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name="scheduledjob",
19
+ name="queue",
20
+ ),
21
+ migrations.AlterField(
22
+ model_name="job",
23
+ name="default_job_queue",
24
+ field=models.ForeignKey(
25
+ on_delete=django.db.models.deletion.PROTECT, related_name="default_for_jobs", to="extras.jobqueue"
26
+ ),
27
+ ),
28
+ ]
@@ -0,0 +1,22 @@
1
+ # Generated by Django 4.2.16 on 2024-11-11 07:04
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("extras", "0119_remove_task_queues_from_job_and_queue_from_scheduled_job"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AddField(
13
+ model_name="job",
14
+ name="is_singleton",
15
+ field=models.BooleanField(default=False),
16
+ ),
17
+ migrations.AddField(
18
+ model_name="job",
19
+ name="is_singleton_override",
20
+ field=models.BooleanField(default=False),
21
+ ),
22
+ ]
@@ -0,0 +1,17 @@
1
+ # Generated by Django 4.2.16 on 2024-12-03 15:32
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("extras", "0120_job_is_singleton_job_is_singleton_override"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AlterField(
13
+ model_name="team",
14
+ name="contacts",
15
+ field=models.ManyToManyField(blank=True, related_name="teams", to="extras.contact"),
16
+ ),
17
+ ]
@@ -8,6 +8,8 @@ from .jobs import (
8
8
  JobButton,
9
9
  JobHook,
10
10
  JobLogEntry,
11
+ JobQueue,
12
+ JobQueueAssignment,
11
13
  JobResult,
12
14
  ScheduledJob,
13
15
  ScheduledJobs,
@@ -66,6 +68,8 @@ __all__ = (
66
68
  "JobButton",
67
69
  "JobHook",
68
70
  "JobLogEntry",
71
+ "JobQueue",
72
+ "JobQueueAssignment",
69
73
  "JobResult",
70
74
  "MetadataChoice",
71
75
  "MetadataType",
@@ -73,8 +77,8 @@ __all__ = (
73
77
  "ObjectChange",
74
78
  "ObjectMetadata",
75
79
  "Relationship",
76
- "RelationshipModel",
77
80
  "RelationshipAssociation",
81
+ "RelationshipModel",
78
82
  "Role",
79
83
  "RoleField",
80
84
  "SavedView",
@@ -169,14 +169,18 @@ class ObjectChange(BaseModel):
169
169
  def get_action_class(self):
170
170
  return ObjectChangeActionChoices.CSS_CLASSES.get(self.action)
171
171
 
172
- def get_next_change(self, user=None):
172
+ def get_next_change(self, user=None, only=None):
173
173
  """Return next change for this changed object, optionally restricting by user view permission"""
174
174
  related_changes = self.get_related_changes(user=user)
175
+ if only:
176
+ related_changes = related_changes.only(*only)
175
177
  return related_changes.filter(time__gt=self.time).order_by("time").first()
176
178
 
177
- def get_prev_change(self, user=None):
179
+ def get_prev_change(self, user=None, only=None):
178
180
  """Return previous change for this changed object, optionally restricting by user view permission"""
179
181
  related_changes = self.get_related_changes(user=user)
182
+ if only:
183
+ related_changes = related_changes.only(*only)
180
184
  return related_changes.filter(time__lt=self.time).order_by("-time").first()
181
185
 
182
186
  def get_related_changes(self, user=None, permission="view"):
@@ -207,7 +211,7 @@ class ObjectChange(BaseModel):
207
211
  prechange = None
208
212
  postchange = None
209
213
 
210
- prior_change = self.get_prev_change()
214
+ prior_change = self.get_prev_change(only=["object_data_v2", "object_data"])
211
215
 
212
216
  if self.action != ObjectChangeActionChoices.ACTION_CREATE and prior_change is not None:
213
217
  prechange = prior_change.object_data_v2
@@ -61,7 +61,7 @@ class Contact(ContactTeamSharedBase):
61
61
  class Team(ContactTeamSharedBase):
62
62
  """A group of Contacts, usable interchangeably with a single Contact in most cases."""
63
63
 
64
- contacts = models.ManyToManyField(to=Contact, related_name="teams")
64
+ contacts = models.ManyToManyField(to=Contact, related_name="teams", blank=True)
65
65
 
66
66
  class Meta(ContactTeamSharedBase.Meta):
67
67
  abstract = False
@@ -1,7 +1,5 @@
1
1
  """Dynamic Groups Models."""
2
2
 
3
- from __future__ import annotations # python 3.8
4
-
5
3
  import logging
6
4
  from typing import Optional
7
5
 
@@ -2,8 +2,11 @@
2
2
 
3
3
  import contextlib
4
4
  from datetime import timedelta
5
+ import json
5
6
  import logging
7
+ import signal
6
8
 
9
+ from billiard.exceptions import SoftTimeLimitExceeded
7
10
  from celery.exceptions import NotRegistered
8
11
  from celery.utils.log import get_logger, LoggingProxy
9
12
  from django.conf import settings
@@ -26,12 +29,12 @@ from nautobot.core.celery import (
26
29
  )
27
30
  from nautobot.core.constants import CHARFIELD_MAX_LENGTH
28
31
  from nautobot.core.models import BaseManager, BaseModel
29
- from nautobot.core.models.fields import JSONArrayField
30
32
  from nautobot.core.models.generics import OrganizationalModel, PrimaryModel
31
33
  from nautobot.core.utils.logging import sanitize
32
34
  from nautobot.extras.choices import (
33
35
  ButtonClassChoices,
34
36
  JobExecutionType,
37
+ JobQueueTypeChoices,
35
38
  JobResultStatusChoices,
36
39
  LogLevelChoices,
37
40
  )
@@ -49,6 +52,8 @@ from nautobot.extras.querysets import JobQuerySet, ScheduledJobExtendedQuerySet
49
52
  from nautobot.extras.utils import (
50
53
  ChangeLoggedModelsQuery,
51
54
  extras_features,
55
+ get_job_queue_worker_count,
56
+ run_kubernetes_job_and_return_job_result,
52
57
  )
53
58
 
54
59
  from .customfields import CustomFieldModel
@@ -135,6 +140,11 @@ class Job(PrimaryModel):
135
140
  default=True, help_text="Whether this job contains sensitive variables"
136
141
  )
137
142
 
143
+ is_singleton = models.BooleanField(
144
+ default=False,
145
+ help_text="Whether this job should fail to run if another instance of this job is already running",
146
+ )
147
+
138
148
  # Additional properties, potentially inherited from the source code
139
149
  # See also the docstring of nautobot.extras.jobs.BaseJob.Meta.
140
150
  approval_required = models.BooleanField(
@@ -164,17 +174,26 @@ class Job(PrimaryModel):
164
174
  help_text="Maximum runtime in seconds before the job will be forcibly terminated."
165
175
  "<br>Set to 0 to use Nautobot system default",
166
176
  )
167
- task_queues = JSONArrayField(
168
- base_field=models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True),
169
- default=list,
170
- blank=True,
171
- help_text="Comma separated list of task queues that this job can run on. A blank list will use the default queue",
172
- )
173
177
  supports_dryrun = models.BooleanField(
174
178
  default=False,
175
179
  editable=False,
176
180
  help_text="If supported, allows the job to bypass approval when running with dryrun argument set to true",
177
181
  )
182
+ job_queues = models.ManyToManyField(
183
+ to="extras.JobQueue",
184
+ related_name="jobs",
185
+ verbose_name="Job Queues",
186
+ help_text="The job queues that this job can be run on",
187
+ through="extras.JobQueueAssignment",
188
+ )
189
+ default_job_queue = models.ForeignKey(
190
+ to="extras.JobQueue",
191
+ related_name="default_for_jobs",
192
+ on_delete=models.PROTECT,
193
+ verbose_name="Default Job Queue",
194
+ null=False,
195
+ blank=False,
196
+ )
178
197
 
179
198
  # Flags to indicate whether the above properties are inherited from the source code or overridden by the database
180
199
  grouping_override = models.BooleanField(
@@ -213,11 +232,18 @@ class Job(PrimaryModel):
213
232
  default=False,
214
233
  help_text="If set, the configured value will remain even if the underlying Job source code changes",
215
234
  )
216
- task_queues_override = models.BooleanField(
235
+ job_queues_override = models.BooleanField(
236
+ default=False,
237
+ help_text="If set, the configured value will remain even if the underlying Job source code changes",
238
+ )
239
+ default_job_queue_override = models.BooleanField(
240
+ default=False,
241
+ help_text="If set, the configured value will remain even if the underlying Job source code changes",
242
+ )
243
+ is_singleton_override = models.BooleanField(
217
244
  default=False,
218
245
  help_text="If set, the configured value will remain even if the underlying Job source code changes",
219
246
  )
220
-
221
247
  objects = BaseManager.from_queryset(JobQuerySet)()
222
248
 
223
249
  documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
@@ -302,6 +328,38 @@ class Job(PrimaryModel):
302
328
  except TypeError as err: # keep 2.0-2.2.2 exception behavior
303
329
  raise NotRegistered from err
304
330
 
331
+ @property
332
+ def task_queues(self):
333
+ """Deprecated backward-compatibility property for the list of queue names for this Job."""
334
+ return self.job_queues.values_list("name", flat=True)
335
+
336
+ @task_queues.setter
337
+ def task_queues(self, value):
338
+ job_queues = []
339
+ # value is going to be a comma separated list of queue names
340
+ if isinstance(value, str):
341
+ value = value.split(",")
342
+ for queue in value:
343
+ try:
344
+ job_queues.append(JobQueue.objects.get(name=queue))
345
+ except JobQueue.DoesNotExist:
346
+ raise ValidationError(f"Job Queue {queue} does not exist in the database.")
347
+ self.job_queues.set(job_queues)
348
+
349
+ @property
350
+ def task_queues_override(self):
351
+ return self.job_queues_override
352
+
353
+ @task_queues_override.setter
354
+ def task_queues_override(self, value):
355
+ if isinstance(value, bool):
356
+ raise ValidationError(
357
+ {
358
+ "task_queues_override": f"{value} is invalid for field task_queues_override, use a boolean value instead"
359
+ }
360
+ )
361
+ self.job_queues_override = value
362
+
305
363
  def clean(self):
306
364
  """For any non-overridden fields, make sure they get reset to the actual underlying class value if known."""
307
365
  from nautobot.extras.jobs import get_job
@@ -312,6 +370,9 @@ class Job(PrimaryModel):
312
370
  if not getattr(self, f"{field_name}_override", False):
313
371
  setattr(self, field_name, getattr(job_class, field_name))
314
372
 
373
+ if not self.job_queues_override:
374
+ self.task_queues = job_class.task_queues or [settings.CELERY_TASK_DEFAULT_QUEUE]
375
+
315
376
  # Protect against invalid input when auto-creating Job records
316
377
  if len(self.module_name) > JOB_MAX_NAME_LENGTH:
317
378
  raise ValidationError(f"Module name may not exceed {JOB_MAX_NAME_LENGTH} characters in length")
@@ -478,6 +539,77 @@ class JobLogEntry(BaseModel):
478
539
  verbose_name_plural = "job log entries"
479
540
 
480
541
 
542
+ #
543
+ # Job Queues
544
+ #
545
+
546
+
547
+ @extras_features(
548
+ "custom_links",
549
+ "custom_validators",
550
+ "export_templates",
551
+ "graphql",
552
+ "webhooks",
553
+ )
554
+ class JobQueue(PrimaryModel):
555
+ """
556
+ A Job Queue represents a structure that is used to manage, organize and schedule jobs for Nautobot workers.
557
+ """
558
+
559
+ name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
560
+ description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
561
+ queue_type = models.CharField(
562
+ max_length=50,
563
+ choices=JobQueueTypeChoices,
564
+ )
565
+ tenant = models.ForeignKey(
566
+ to="tenancy.Tenant",
567
+ on_delete=models.PROTECT,
568
+ related_name="job_queues",
569
+ blank=True,
570
+ null=True,
571
+ )
572
+
573
+ documentation_static_path = "docs/user-guide/platform-functionality/jobs/jobqueue.html"
574
+
575
+ class Meta:
576
+ ordering = ["name"]
577
+
578
+ def __str__(self):
579
+ return f"{self.queue_type}: {self.name}"
580
+
581
+ @property
582
+ def display(self):
583
+ if self.queue_type != JobQueueTypeChoices.TYPE_CELERY:
584
+ return f"{self.queue_type}: {self.name}"
585
+ worker_count = get_job_queue_worker_count(job_queue=self)
586
+ workers = "worker" if worker_count == 1 else "workers"
587
+ return f"{self.queue_type}: {self.name} ({worker_count} {workers})"
588
+
589
+
590
+ @extras_features(
591
+ "custom_links",
592
+ "custom_validators",
593
+ "export_templates",
594
+ "graphql",
595
+ )
596
+ class JobQueueAssignment(BaseModel):
597
+ """
598
+ Through table model that represents the m2m relationship between jobs and job queues.
599
+ """
600
+
601
+ job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name="job_queue_assignments")
602
+ job_queue = models.ForeignKey(JobQueue, on_delete=models.CASCADE, related_name="job_assignments")
603
+ is_metadata_associable_model = False
604
+
605
+ class Meta:
606
+ unique_together = ["job", "job_queue"]
607
+ ordering = ["job", "job_queue"]
608
+
609
+ def __str__(self):
610
+ return f"{self.job}: {self.job_queue}"
611
+
612
+
481
613
  #
482
614
  # Job results
483
615
  #
@@ -616,7 +748,7 @@ class JobResult(BaseModel, CustomFieldModel):
616
748
  )
617
749
 
618
750
  @classmethod
619
- def execute_job(cls, job_model, user, *job_args, celery_kwargs=None, profile=False, **job_kwargs):
751
+ def execute_job(cls, job_model, user, *job_args, celery_kwargs=None, profile=False, job_result=None, **job_kwargs):
620
752
  """
621
753
  Create a JobResult instance and run a job in the current process, blocking until the job finishes.
622
754
 
@@ -628,6 +760,7 @@ class JobResult(BaseModel, CustomFieldModel):
628
760
  user (User): User object to link to the JobResult instance
629
761
  celery_kwargs (dict, optional): Dictionary of kwargs to pass as **kwargs to Celery when job is run
630
762
  profile (bool, optional): Whether to run cProfile on the job execution
763
+ job_result (JobResult, optional): Existing JobResult with status PENDING, used in kubernetes job execution
631
764
  *job_args: positional args passed to the job task
632
765
  **job_kwargs: keyword args passed to the job task
633
766
 
@@ -635,7 +768,14 @@ class JobResult(BaseModel, CustomFieldModel):
635
768
  JobResult instance
636
769
  """
637
770
  return cls.enqueue_job(
638
- job_model, user, *job_args, celery_kwargs=celery_kwargs, profile=profile, synchronous=True, **job_kwargs
771
+ job_model,
772
+ user,
773
+ *job_args,
774
+ celery_kwargs=celery_kwargs,
775
+ profile=profile,
776
+ job_result=job_result,
777
+ synchronous=True,
778
+ **job_kwargs,
639
779
  )
640
780
 
641
781
  @classmethod
@@ -648,10 +788,12 @@ class JobResult(BaseModel, CustomFieldModel):
648
788
  profile=False,
649
789
  schedule=None,
650
790
  task_queue=None,
791
+ job_result=None,
651
792
  synchronous=False,
793
+ ignore_singleton_lock=False,
652
794
  **job_kwargs,
653
795
  ):
654
- """Create a JobResult instance and enqueue a job to be executed asynchronously by a Celery worker.
796
+ """Create/Modify a JobResult instance and enqueue a job to be executed asynchronously by a Celery worker.
655
797
 
656
798
  Args:
657
799
  job_model (Job): The Job to be enqueued for execution.
@@ -660,7 +802,11 @@ class JobResult(BaseModel, CustomFieldModel):
660
802
  profile (bool, optional): If True, dump cProfile stats on the job execution.
661
803
  schedule (ScheduledJob, optional): ScheduledJob instance to link to the JobResult. Cannot be used with synchronous=True.
662
804
  task_queue (str, optional): The celery queue to send the job to. If not set, use the default celery queue.
805
+ job_result (JobResult, optional): Existing JobResult with status PENDING, to be modified and to be used in kubernetes job execution.
663
806
  synchronous (bool, optional): If True, run the job in the current process, blocking until the job completes.
807
+ ignore_singleton_lock (bool, optional): If True, invalidate the singleton lock before running the job.
808
+ This allows singleton jobs to run twice, or makes it possible to remove the lock when the first instance
809
+ of the job failed to remove it for any reason.
664
810
  *job_args: positional args passed to the job task (UNUSED)
665
811
  **job_kwargs: keyword args passed to the job task
666
812
 
@@ -672,20 +818,40 @@ class JobResult(BaseModel, CustomFieldModel):
672
818
  if schedule is not None and synchronous:
673
819
  raise ValueError("Scheduled jobs cannot be run synchronously")
674
820
 
675
- job_result = cls.objects.create(
676
- name=job_model.name,
677
- job_model=job_model,
678
- scheduled_job=schedule,
679
- user=user,
680
- )
821
+ if job_result is None:
822
+ job_result = cls.objects.create(
823
+ name=job_model.name,
824
+ job_model=job_model,
825
+ scheduled_job=schedule,
826
+ user=user,
827
+ )
828
+ else:
829
+ if job_result.user != user:
830
+ raise ValueError(
831
+ f"There is a mismatch between the user specified {user} and the user associated with the job result {job_result.user}"
832
+ )
833
+ if job_result.job_model != job_model:
834
+ raise ValueError(
835
+ f"There is a mismatch between the job specified {job_model} and the job associated with the job result {job_result.job_model}"
836
+ )
681
837
 
682
838
  if task_queue is None:
683
- task_queue = settings.CELERY_TASK_DEFAULT_QUEUE
839
+ task_queue = job_model.default_job_queue.name
840
+
841
+ job_queue = JobQueue.objects.get(name=task_queue)
842
+ # Kubernetes Job Queue logic
843
+ # As we execute Kubernetes jobs, we want to execute `run_kubernetes_job_and_return_job_result`
844
+ # the first time the kubernetes job is enqueued to spin up the kubernetes pod.
845
+ # And from the kubernetes pod, we specify "--local"/synchronous=True
846
+ # so that `run_kubernetes_job_and_return_job_result` is not executed again and the job will be run locally.
847
+ if job_queue.queue_type == JobQueueTypeChoices.TYPE_KUBERNETES and not synchronous:
848
+ return run_kubernetes_job_and_return_job_result(job_queue, job_result, json.dumps(job_kwargs))
684
849
 
685
850
  job_celery_kwargs = {
686
851
  "nautobot_job_job_model_id": job_model.id,
687
852
  "nautobot_job_profile": profile,
688
853
  "nautobot_job_user_id": user.id,
854
+ "nautobot_job_ignore_singleton_lock": ignore_singleton_lock,
689
855
  "queue": task_queue,
690
856
  }
691
857
 
@@ -712,12 +878,24 @@ class JobResult(BaseModel, CustomFieldModel):
712
878
  redirect_logger = get_logger("celery.redirected")
713
879
  proxy = LoggingProxy(redirect_logger, app.conf.worker_redirect_stdouts_level)
714
880
  with contextlib.redirect_stdout(proxy), contextlib.redirect_stderr(proxy):
715
- eager_result = run_job.apply(
716
- args=[job_model.class_path, *job_args],
717
- kwargs=job_kwargs,
718
- task_id=str(job_result.id),
719
- **job_celery_kwargs,
720
- )
881
+
882
+ def alarm_handler(*args, **kwargs):
883
+ raise SoftTimeLimitExceeded()
884
+
885
+ # Set alarm_handler to be called on a SIGALRM, and schedule a SIGALRM based on the soft time limit
886
+ signal.signal(signal.SIGALRM, alarm_handler)
887
+ signal.alarm(int(job_model.soft_time_limit) or settings.CELERY_TASK_SOFT_TIME_LIMIT)
888
+
889
+ try:
890
+ eager_result = run_job.apply(
891
+ args=[job_model.class_path, *job_args],
892
+ kwargs=job_kwargs,
893
+ task_id=str(job_result.id),
894
+ **job_celery_kwargs,
895
+ )
896
+ finally:
897
+ # Cancel the scheduled SIGALRM if it hasn't fired already
898
+ signal.alarm(0)
721
899
 
722
900
  # copy fields from eager result to job result
723
901
  job_result.refresh_from_db()
@@ -728,9 +906,15 @@ class JobResult(BaseModel, CustomFieldModel):
728
906
  "exc_message": sanitize(str(eager_result.result)),
729
907
  }
730
908
  else:
731
- job_result.result = sanitize(eager_result.result)
909
+ if eager_result.result is not None:
910
+ job_result.result = sanitize(eager_result.result)
911
+ else:
912
+ job_result.result = None
732
913
  job_result.status = eager_result.status
733
- job_result.traceback = sanitize(eager_result.traceback)
914
+ if eager_result.traceback is not None:
915
+ job_result.traceback = sanitize(eager_result.traceback)
916
+ else:
917
+ job_result.traceback = None
734
918
  job_result.date_done = timezone.now()
735
919
  job_result.save()
736
920
  else:
@@ -927,13 +1111,13 @@ class ScheduledJob(BaseModel):
927
1111
  args = models.JSONField(blank=True, default=list, encoder=NautobotKombuJSONEncoder)
928
1112
  kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
929
1113
  celery_kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
930
- queue = models.CharField(
931
- max_length=CHARFIELD_MAX_LENGTH,
1114
+ job_queue = models.ForeignKey(
1115
+ to="extras.JobQueue",
1116
+ on_delete=models.SET_NULL,
1117
+ related_name="scheduled_jobs",
1118
+ null=True,
932
1119
  blank=True,
933
- default="",
934
- verbose_name="Queue Override",
935
- help_text="Queue defined in CELERY_TASK_QUEUES. Leave empty for default queuing.",
936
- db_index=True,
1120
+ verbose_name="Job Queue Override",
937
1121
  )
938
1122
  one_off = models.BooleanField(
939
1123
  default=False,
@@ -1065,6 +1249,19 @@ class ScheduledJob(BaseModel):
1065
1249
 
1066
1250
  return self.to_cron()
1067
1251
 
1252
+ @property
1253
+ def queue(self):
1254
+ """Deprecated backward-compatibility property for the queue name this job is scheduled for."""
1255
+ return self.job_queue.name if self.job_queue else ""
1256
+
1257
+ @queue.setter
1258
+ def queue(self, value):
1259
+ if value:
1260
+ try:
1261
+ self.job_queue = JobQueue.objects.get(name=value)
1262
+ except JobQueue.DoesNotExist:
1263
+ raise ValidationError(f"Job Queue {value} does not exist in the database.")
1264
+
1068
1265
  @staticmethod
1069
1266
  def earliest_possible_time():
1070
1267
  return timezone.now() + timedelta(seconds=15)
@@ -1108,6 +1305,7 @@ class ScheduledJob(BaseModel):
1108
1305
  profile=False,
1109
1306
  approval_required=False,
1110
1307
  task_queue=None,
1308
+ ignore_singleton_lock=False,
1111
1309
  **job_kwargs,
1112
1310
  ):
1113
1311
  """
@@ -1128,6 +1326,7 @@ class ScheduledJob(BaseModel):
1128
1326
  profile (bool, optional): Flag indicating whether to profile the job. Defaults to False.
1129
1327
  approval_required (bool, optional): Flag indicating if approval is required. Defaults to False.
1130
1328
  task_queue (str, optional): The task queue for the job. Defaults to None, which will use the configured default celery queue.
1329
+ ignore_singleton_lock (bool, optional): Flag indicating whether to ignore singleton locks. Defaults to False.
1131
1330
  **job_kwargs: Additional keyword arguments to pass to the job.
1132
1331
 
1133
1332
  Returns:
@@ -1146,6 +1345,7 @@ class ScheduledJob(BaseModel):
1146
1345
  celery_kwargs = {
1147
1346
  "nautobot_job_profile": profile,
1148
1347
  "queue": task_queue,
1348
+ "nautobot_job_ignore_singleton_lock": ignore_singleton_lock,
1149
1349
  }
1150
1350
  if job_model.soft_time_limit > 0:
1151
1351
  celery_kwargs["soft_time_limit"] = job_model.soft_time_limit