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

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

Potentially problematic release.


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

Files changed (675) hide show
  1. nautobot/__init__.py +15 -0
  2. nautobot/apps/api.py +0 -2
  3. nautobot/apps/config.py +32 -3
  4. nautobot/apps/events.py +19 -0
  5. nautobot/apps/exceptions.py +0 -2
  6. nautobot/apps/ui.py +44 -9
  7. nautobot/apps/utils.py +0 -8
  8. nautobot/apps/views.py +2 -0
  9. nautobot/circuits/navigation.py +0 -57
  10. nautobot/circuits/tables.py +1 -2
  11. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
  12. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
  13. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
  15. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
  16. nautobot/circuits/tests/integration/test_relationships.py +33 -24
  17. nautobot/circuits/tests/test_filters.py +4 -8
  18. nautobot/circuits/views.py +143 -26
  19. nautobot/cloud/factory.py +4 -1
  20. nautobot/cloud/models.py +1 -1
  21. nautobot/cloud/tests/test_filters.py +5 -4
  22. nautobot/core/api/fields.py +5 -5
  23. nautobot/core/api/metadata.py +28 -256
  24. nautobot/core/api/pagination.py +3 -2
  25. nautobot/core/api/renderers.py +3 -0
  26. nautobot/core/api/serializers.py +24 -244
  27. nautobot/core/api/urls.py +3 -4
  28. nautobot/core/api/utils.py +0 -62
  29. nautobot/core/api/views.py +48 -158
  30. nautobot/core/apps/__init__.py +22 -578
  31. nautobot/core/celery/__init__.py +13 -0
  32. nautobot/core/celery/log.py +4 -4
  33. nautobot/core/celery/schedulers.py +48 -3
  34. nautobot/core/cli/__init__.py +8 -0
  35. nautobot/core/constants.py +7 -0
  36. nautobot/core/events/__init__.py +116 -0
  37. nautobot/core/events/base.py +27 -0
  38. nautobot/core/events/exceptions.py +10 -0
  39. nautobot/core/events/redis_broker.py +48 -0
  40. nautobot/core/events/syslog_broker.py +19 -0
  41. nautobot/core/exceptions.py +0 -6
  42. nautobot/core/filters.py +16 -21
  43. nautobot/core/fixtures/user-data.json +59 -0
  44. nautobot/core/forms/fields.py +53 -8
  45. nautobot/core/forms/utils.py +2 -1
  46. nautobot/core/graphql/schema.py +3 -1
  47. nautobot/core/graphql/types.py +1 -1
  48. nautobot/core/jobs/__init__.py +4 -4
  49. nautobot/core/jobs/cleanup.py +13 -49
  50. nautobot/core/jobs/groups.py +1 -1
  51. nautobot/core/management/commands/generate_test_data.py +21 -0
  52. nautobot/core/management/commands/validate_models.py +1 -1
  53. nautobot/core/middleware.py +16 -0
  54. nautobot/core/models/__init__.py +1 -1
  55. nautobot/core/models/fields.py +11 -7
  56. nautobot/core/models/query_functions.py +2 -2
  57. nautobot/core/models/tree_queries.py +3 -6
  58. nautobot/core/settings.py +44 -7
  59. nautobot/core/settings.yaml +86 -8
  60. nautobot/core/tables.py +15 -65
  61. nautobot/core/tasks.py +1 -1
  62. nautobot/core/templates/components/button/default.html +7 -0
  63. nautobot/core/templates/components/button/dropdown.html +20 -0
  64. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  65. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  66. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  67. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  68. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  69. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  70. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  71. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  72. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  73. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  74. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  75. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  76. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  77. nautobot/core/templates/components/panel/panel.html +16 -0
  78. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  79. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  80. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  81. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  82. nautobot/core/templates/generic/object_retrieve.html +28 -17
  83. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  84. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  85. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  86. nautobot/core/templates/inc/footer.html +1 -0
  87. nautobot/core/templates/inc/media.html +0 -3
  88. nautobot/core/templates/inc/nav_menu.html +1 -1
  89. nautobot/core/templates/inc/relationships_panel.html +1 -1
  90. nautobot/core/templates/nautobot_config.py.j2 +3 -3
  91. nautobot/core/templates/panel_table.html +12 -0
  92. nautobot/core/templates/search.html +0 -7
  93. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  94. nautobot/core/templatetags/helpers.py +101 -12
  95. nautobot/core/templatetags/ui_framework.py +40 -0
  96. nautobot/core/testing/api.py +23 -128
  97. nautobot/core/testing/context.py +18 -0
  98. nautobot/core/testing/filters.py +41 -58
  99. nautobot/core/testing/mixins.py +2 -7
  100. nautobot/core/testing/views.py +25 -123
  101. nautobot/core/tests/integration/test_app_home.py +1 -0
  102. nautobot/core/tests/integration/test_app_navbar.py +1 -0
  103. nautobot/core/tests/integration/test_filters.py +2 -0
  104. nautobot/core/tests/integration/test_home.py +1 -0
  105. nautobot/core/tests/integration/test_navbar.py +1 -0
  106. nautobot/core/tests/integration/test_view_authentication.py +1 -2
  107. nautobot/core/tests/nautobot_config.py +198 -0
  108. nautobot/core/tests/runner.py +3 -3
  109. nautobot/core/tests/test_api.py +82 -201
  110. nautobot/core/tests/test_csv.py +3 -25
  111. nautobot/core/tests/test_events.py +214 -0
  112. nautobot/core/tests/test_jinja_filters.py +1 -0
  113. nautobot/core/tests/test_jobs.py +84 -13
  114. nautobot/core/tests/test_navigations.py +7 -241
  115. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  116. nautobot/core/tests/test_ui.py +150 -0
  117. nautobot/core/tests/test_utils.py +0 -25
  118. nautobot/core/tests/test_views.py +123 -31
  119. nautobot/core/ui/__init__.py +0 -0
  120. nautobot/core/ui/base.py +11 -0
  121. nautobot/core/ui/choices.py +44 -0
  122. nautobot/core/ui/homepage.py +167 -0
  123. nautobot/core/ui/nav.py +279 -0
  124. nautobot/core/ui/object_detail.py +1841 -0
  125. nautobot/core/ui/utils.py +36 -0
  126. nautobot/core/urls.py +4 -9
  127. nautobot/core/utils/config.py +30 -3
  128. nautobot/core/utils/lookup.py +20 -13
  129. nautobot/core/views/__init__.py +6 -1
  130. nautobot/core/views/generic.py +47 -52
  131. nautobot/core/views/mixins.py +15 -25
  132. nautobot/core/views/paginator.py +8 -5
  133. nautobot/core/views/renderers.py +3 -3
  134. nautobot/core/views/utils.py +11 -0
  135. nautobot/core/wsgi.py +3 -3
  136. nautobot/dcim/api/serializers.py +80 -179
  137. nautobot/dcim/api/urls.py +5 -0
  138. nautobot/dcim/api/views.py +17 -4
  139. nautobot/dcim/apps.py +1 -0
  140. nautobot/dcim/choices.py +28 -0
  141. nautobot/dcim/factory.py +58 -0
  142. nautobot/dcim/filters/__init__.py +197 -24
  143. nautobot/dcim/forms.py +203 -12
  144. nautobot/dcim/graphql/types.py +2 -2
  145. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  146. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  147. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  148. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  149. nautobot/dcim/models/__init__.py +4 -0
  150. nautobot/dcim/models/device_component_templates.py +2 -2
  151. nautobot/dcim/models/device_components.py +20 -22
  152. nautobot/dcim/models/devices.py +173 -4
  153. nautobot/dcim/models/locations.py +3 -3
  154. nautobot/dcim/models/power.py +5 -6
  155. nautobot/dcim/models/racks.py +6 -6
  156. nautobot/dcim/navigation.py +25 -224
  157. nautobot/dcim/signals.py +44 -0
  158. nautobot/dcim/tables/__init__.py +5 -3
  159. nautobot/dcim/tables/devices.py +96 -2
  160. nautobot/dcim/tables/devicetypes.py +2 -2
  161. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  162. nautobot/dcim/templates/dcim/controller_create.html +1 -0
  163. nautobot/dcim/templates/dcim/controller_retrieve.html +5 -1
  164. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  165. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +66 -0
  166. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +46 -0
  167. nautobot/dcim/templates/dcim/device/base.html +6 -42
  168. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  169. nautobot/dcim/templates/dcim/device.html +3 -1
  170. nautobot/dcim/templates/dcim/interface.html +1 -0
  171. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  172. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  173. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  174. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +76 -0
  175. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +34 -0
  176. nautobot/dcim/tests/test_api.py +172 -61
  177. nautobot/dcim/tests/test_filters.py +171 -109
  178. nautobot/dcim/tests/test_forms.py +2 -51
  179. nautobot/dcim/tests/test_graphql.py +0 -52
  180. nautobot/dcim/tests/test_models.py +126 -4
  181. nautobot/dcim/tests/test_signals.py +1 -0
  182. nautobot/dcim/tests/test_views.py +103 -11
  183. nautobot/dcim/urls.py +72 -27
  184. nautobot/dcim/utils.py +2 -2
  185. nautobot/dcim/views.py +369 -62
  186. nautobot/extras/api/customfields.py +2 -2
  187. nautobot/extras/api/serializers.py +91 -75
  188. nautobot/extras/api/urls.py +4 -0
  189. nautobot/extras/api/views.py +78 -15
  190. nautobot/extras/choices.py +13 -0
  191. nautobot/extras/constants.py +0 -1
  192. nautobot/extras/context_managers.py +23 -6
  193. nautobot/extras/datasources/git.py +4 -1
  194. nautobot/extras/factory.py +27 -0
  195. nautobot/extras/filters/__init__.py +59 -0
  196. nautobot/extras/forms/forms.py +125 -30
  197. nautobot/extras/forms/mixins.py +3 -11
  198. nautobot/extras/graphql/types.py +25 -1
  199. nautobot/extras/group_sync.py +3 -3
  200. nautobot/extras/health_checks.py +2 -1
  201. nautobot/extras/jobs.py +62 -26
  202. nautobot/extras/management/__init__.py +1 -0
  203. nautobot/extras/management/commands/runjob.py +7 -79
  204. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  205. nautobot/extras/management/utils.py +87 -0
  206. nautobot/extras/managers.py +1 -3
  207. nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
  208. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  209. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  210. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  211. nautobot/extras/models/__init__.py +4 -0
  212. nautobot/extras/models/change_logging.py +7 -3
  213. nautobot/extras/models/customfields.py +11 -12
  214. nautobot/extras/models/groups.py +9 -13
  215. nautobot/extras/models/jobs.py +218 -37
  216. nautobot/extras/models/models.py +2 -2
  217. nautobot/extras/models/relationships.py +69 -1
  218. nautobot/extras/models/secrets.py +5 -0
  219. nautobot/extras/navigation.py +20 -262
  220. nautobot/extras/plugins/__init__.py +56 -32
  221. nautobot/extras/plugins/marketplace_manifest.yml +450 -0
  222. nautobot/extras/plugins/urls.py +1 -0
  223. nautobot/extras/plugins/views.py +48 -1
  224. nautobot/extras/signals.py +39 -1
  225. nautobot/extras/tables.py +40 -6
  226. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  227. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  228. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  229. nautobot/extras/templates/extras/job_detail.html +36 -6
  230. nautobot/extras/templates/extras/job_edit.html +5 -2
  231. nautobot/extras/templates/extras/job_list.html +2 -7
  232. nautobot/extras/templates/extras/jobqueue_retrieve.html +44 -0
  233. nautobot/extras/templates/extras/marketplace.html +278 -0
  234. nautobot/extras/templates/extras/plugins_list.html +35 -1
  235. nautobot/extras/templates/extras/plugins_tiles.html +79 -0
  236. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  237. nautobot/extras/templates/extras/secret.html +0 -65
  238. nautobot/extras/templates/extras/secret_check.js +16 -0
  239. nautobot/extras/templates/extras/secret_create.html +114 -0
  240. nautobot/extras/templates/extras/secret_edit.html +1 -114
  241. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  242. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  243. nautobot/extras/templatetags/job_buttons.py +5 -4
  244. nautobot/extras/templatetags/plugins.py +69 -6
  245. nautobot/extras/test_jobs/api_test_job.py +1 -1
  246. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  247. nautobot/extras/test_jobs/dry_run.py +1 -1
  248. nautobot/extras/test_jobs/fail.py +5 -5
  249. nautobot/extras/test_jobs/file_output.py +1 -1
  250. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  251. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  252. nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
  253. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  254. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  255. nautobot/extras/test_jobs/log_redaction.py +1 -1
  256. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  257. nautobot/extras/test_jobs/modify_db.py +1 -1
  258. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  259. nautobot/extras/test_jobs/object_var_required.py +1 -1
  260. nautobot/extras/test_jobs/object_vars.py +1 -1
  261. nautobot/extras/test_jobs/pass.py +3 -3
  262. nautobot/extras/test_jobs/profiling.py +1 -1
  263. nautobot/extras/test_jobs/relative_import.py +3 -3
  264. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  265. nautobot/extras/test_jobs/task_queues.py +1 -1
  266. nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
  267. nautobot/extras/tests/test_api.py +157 -55
  268. nautobot/extras/tests/test_context_managers.py +4 -1
  269. nautobot/extras/tests/test_customfields.py +1 -1
  270. nautobot/extras/tests/test_datasources.py +1 -2
  271. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  272. nautobot/extras/tests/test_filters.py +219 -535
  273. nautobot/extras/tests/test_forms.py +1 -20
  274. nautobot/extras/tests/test_job_variables.py +73 -152
  275. nautobot/extras/tests/test_jobs.py +43 -54
  276. nautobot/extras/tests/test_models.py +71 -16
  277. nautobot/extras/tests/test_relationships.py +5 -2
  278. nautobot/extras/tests/test_utils.py +23 -2
  279. nautobot/extras/tests/test_views.py +183 -43
  280. nautobot/extras/tests/test_webhooks.py +2 -1
  281. nautobot/extras/urls.py +2 -20
  282. nautobot/extras/utils.py +118 -4
  283. nautobot/extras/views.py +203 -92
  284. nautobot/extras/webhooks.py +5 -2
  285. nautobot/ipam/api/fields.py +3 -3
  286. nautobot/ipam/api/serializers.py +36 -137
  287. nautobot/ipam/api/views.py +93 -62
  288. nautobot/ipam/lookups.py +62 -101
  289. nautobot/ipam/models.py +11 -63
  290. nautobot/ipam/navigation.py +0 -90
  291. nautobot/ipam/querysets.py +2 -2
  292. nautobot/ipam/tables.py +6 -20
  293. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  294. nautobot/ipam/templates/ipam/vrf.html +0 -47
  295. nautobot/ipam/tests/test_api.py +8 -419
  296. nautobot/ipam/tests/test_filters.py +39 -119
  297. nautobot/ipam/tests/test_forms.py +47 -51
  298. nautobot/ipam/tests/test_migrations.py +30 -30
  299. nautobot/ipam/tests/test_models.py +0 -41
  300. nautobot/ipam/tests/test_querysets.py +1 -63
  301. nautobot/ipam/urls.py +3 -69
  302. nautobot/ipam/utils/__init__.py +0 -24
  303. nautobot/ipam/views.py +153 -198
  304. nautobot/project-static/css/base.css +38 -3
  305. nautobot/project-static/docs/404.html +421 -19
  306. nautobot/project-static/docs/apps/index.html +421 -19
  307. nautobot/project-static/docs/apps/nautobot-apps.html +421 -19
  308. nautobot/project-static/docs/assets/extra.css +5 -1
  309. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
  310. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
  311. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +421 -19
  312. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +421 -19
  313. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +421 -172
  314. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +421 -19
  315. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +421 -19
  316. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +425 -21
  317. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +421 -19
  318. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +424 -22
  319. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9809 -0
  320. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +424 -63
  321. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +421 -19
  322. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +421 -19
  323. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +457 -20
  324. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +421 -19
  325. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +425 -25
  326. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +457 -19
  327. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +421 -19
  328. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +421 -19
  329. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +425 -215
  330. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +430 -342
  331. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5799 -1054
  332. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +421 -19
  333. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +447 -176
  334. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +460 -21
  335. nautobot/project-static/docs/development/apps/api/configuration-view.html +421 -19
  336. nautobot/project-static/docs/development/apps/api/database-backend-config.html +421 -19
  337. nautobot/project-static/docs/development/apps/api/models/django-admin.html +421 -19
  338. nautobot/project-static/docs/development/apps/api/models/global-search.html +421 -19
  339. nautobot/project-static/docs/development/apps/api/models/graphql.html +421 -19
  340. nautobot/project-static/docs/development/apps/api/models/index.html +421 -19
  341. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +421 -19
  342. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +421 -19
  343. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +421 -19
  344. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +421 -19
  345. nautobot/project-static/docs/development/apps/api/platform-features/index.html +421 -19
  346. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +421 -19
  347. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +421 -19
  348. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +421 -19
  349. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +421 -19
  350. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +424 -41
  351. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +421 -19
  352. nautobot/project-static/docs/development/apps/api/prometheus.html +421 -19
  353. nautobot/project-static/docs/development/apps/api/setup.html +425 -155
  354. nautobot/project-static/docs/development/apps/api/testing.html +421 -19
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +421 -19
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +421 -19
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +421 -19
  358. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +421 -19
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +701 -130
  360. nautobot/project-static/docs/development/apps/api/views/base-template.html +421 -19
  361. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +421 -19
  362. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +421 -19
  363. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +421 -19
  364. nautobot/project-static/docs/development/apps/api/views/index.html +423 -20
  365. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +425 -19
  366. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +451 -19
  367. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +421 -19
  368. nautobot/project-static/docs/development/apps/api/views/notes.html +421 -19
  369. nautobot/project-static/docs/development/apps/api/views/rest-api.html +421 -19
  370. nautobot/project-static/docs/development/apps/api/views/urls.html +421 -19
  371. nautobot/project-static/docs/development/apps/index.html +421 -19
  372. nautobot/project-static/docs/development/apps/migration/code-updates.html +422 -52
  373. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +422 -20
  374. nautobot/project-static/docs/development/apps/migration/from-v1.html +421 -19
  375. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +421 -19
  376. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +421 -19
  377. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +421 -19
  378. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +424 -22
  379. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9219 -0
  380. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9333 -0
  381. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9474 -0
  382. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9517 -0
  383. nautobot/project-static/docs/development/apps/porting-from-netbox.html +424 -22
  384. nautobot/project-static/docs/development/core/application-registry.html +421 -19
  385. nautobot/project-static/docs/development/core/best-practices.html +421 -19
  386. nautobot/project-static/docs/development/core/bootstrap-ui.html +421 -19
  387. nautobot/project-static/docs/development/core/caching.html +421 -19
  388. nautobot/project-static/docs/development/core/controllers.html +423 -19
  389. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +490 -45
  390. nautobot/project-static/docs/development/core/generic-views.html +421 -19
  391. nautobot/project-static/docs/development/core/getting-started.html +566 -179
  392. nautobot/project-static/docs/development/core/homepage.html +432 -30
  393. nautobot/project-static/docs/development/core/index.html +421 -19
  394. nautobot/project-static/docs/development/core/local-k8s.html +9453 -0
  395. nautobot/project-static/docs/development/core/model-checklist.html +424 -22
  396. nautobot/project-static/docs/development/core/model-features.html +421 -19
  397. nautobot/project-static/docs/development/core/natural-keys.html +421 -19
  398. nautobot/project-static/docs/development/core/navigation-menu.html +438 -26
  399. nautobot/project-static/docs/development/core/release-checklist.html +435 -45
  400. nautobot/project-static/docs/development/core/role-internals.html +421 -19
  401. nautobot/project-static/docs/development/core/settings.html +421 -19
  402. nautobot/project-static/docs/development/core/style-guide.html +421 -19
  403. nautobot/project-static/docs/development/core/templates.html +431 -22
  404. nautobot/project-static/docs/development/core/testing.html +421 -19
  405. nautobot/project-static/docs/development/core/ui-component-framework.html +11020 -0
  406. nautobot/project-static/docs/development/core/user-preferences.html +424 -22
  407. nautobot/project-static/docs/development/index.html +421 -19
  408. nautobot/project-static/docs/development/jobs/index.html +546 -160
  409. nautobot/project-static/docs/development/jobs/migration/from-v1.html +421 -19
  410. nautobot/project-static/docs/index.html +421 -19
  411. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  412. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  413. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  414. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  415. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  416. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  417. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  418. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  419. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  420. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  421. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  422. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  423. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  424. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  425. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  426. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  427. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  428. nautobot/project-static/docs/objects.inv +0 -0
  429. nautobot/project-static/docs/overview/application_stack.html +426 -20
  430. nautobot/project-static/docs/overview/design_philosophy.html +421 -19
  431. nautobot/project-static/docs/release-notes/index.html +445 -22
  432. nautobot/project-static/docs/release-notes/version-1.0.html +421 -19
  433. nautobot/project-static/docs/release-notes/version-1.1.html +421 -19
  434. nautobot/project-static/docs/release-notes/version-1.2.html +421 -19
  435. nautobot/project-static/docs/release-notes/version-1.3.html +421 -19
  436. nautobot/project-static/docs/release-notes/version-1.4.html +421 -19
  437. nautobot/project-static/docs/release-notes/version-1.5.html +421 -19
  438. nautobot/project-static/docs/release-notes/version-1.6.html +634 -667
  439. nautobot/project-static/docs/release-notes/version-2.0.html +421 -19
  440. nautobot/project-static/docs/release-notes/version-2.1.html +421 -19
  441. nautobot/project-static/docs/release-notes/version-2.2.html +421 -19
  442. nautobot/project-static/docs/release-notes/version-2.3.html +684 -886
  443. nautobot/project-static/docs/release-notes/version-2.4.html +10007 -0
  444. nautobot/project-static/docs/requirements.txt +2 -2
  445. nautobot/project-static/docs/search/search_index.json +1 -1
  446. nautobot/project-static/docs/sitemap.xml +334 -270
  447. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  448. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +421 -19
  449. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +421 -19
  450. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +423 -21
  451. nautobot/project-static/docs/user-guide/administration/configuration/index.html +433 -32
  452. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +421 -19
  453. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +765 -180
  454. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +421 -19
  455. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +421 -19
  456. nautobot/project-static/docs/user-guide/administration/guides/docker.html +434 -29
  457. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +421 -19
  458. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +421 -19
  459. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +421 -19
  460. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +421 -19
  461. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +421 -19
  462. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +421 -19
  463. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +421 -19
  464. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +421 -19
  465. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +421 -19
  466. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +421 -19
  467. nautobot/project-static/docs/user-guide/administration/installation/index.html +426 -20
  468. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +421 -19
  469. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +421 -19
  470. nautobot/project-static/docs/user-guide/administration/installation/services.html +421 -19
  471. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +421 -19
  472. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +442 -41
  473. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +435 -66
  474. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +435 -66
  475. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +421 -19
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +421 -19
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +421 -19
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +421 -19
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +421 -19
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +421 -19
  481. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +421 -19
  482. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  483. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +421 -19
  484. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +427 -21
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +421 -19
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +421 -19
  487. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +421 -19
  488. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +421 -19
  489. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +421 -19
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +421 -19
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +421 -19
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +421 -19
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +421 -19
  494. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +421 -19
  495. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +421 -19
  496. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +421 -19
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +421 -19
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +421 -19
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +421 -19
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +421 -19
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +421 -19
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +457 -20
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +447 -22
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +421 -19
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +421 -19
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +421 -19
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +421 -19
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +421 -19
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +421 -19
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +421 -19
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +421 -19
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +421 -19
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +421 -19
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +421 -19
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +421 -19
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +421 -19
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +421 -19
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +421 -19
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +421 -19
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +421 -19
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +421 -19
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +421 -19
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +421 -19
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +421 -19
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +421 -19
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +421 -19
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +421 -19
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +421 -19
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +421 -19
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +421 -19
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +421 -19
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +421 -19
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +421 -19
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +421 -19
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +421 -19
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +421 -19
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +421 -19
  538. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9333 -0
  539. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +424 -22
  540. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +421 -19
  541. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +421 -19
  542. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +421 -19
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +421 -19
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +421 -19
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +421 -19
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +421 -19
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +421 -19
  548. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +421 -19
  549. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +421 -19
  550. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +421 -19
  551. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +421 -19
  552. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +421 -19
  553. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +421 -19
  554. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +421 -19
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +421 -19
  556. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +421 -19
  557. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +421 -19
  558. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +421 -19
  559. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +424 -22
  560. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9271 -0
  561. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9175 -0
  562. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9169 -0
  563. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9235 -0
  564. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +421 -19
  565. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +421 -19
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +421 -19
  567. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +421 -19
  568. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +421 -19
  569. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +421 -19
  570. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +421 -19
  571. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +421 -19
  572. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +421 -19
  573. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +421 -19
  574. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +421 -19
  575. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +421 -19
  576. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +421 -19
  577. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +421 -19
  578. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +421 -19
  579. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +421 -19
  580. nautobot/project-static/docs/user-guide/index.html +421 -19
  581. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +424 -22
  582. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +421 -19
  583. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +421 -19
  584. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +421 -19
  585. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +421 -19
  586. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9575 -0
  587. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +424 -22
  588. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +421 -19
  589. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +421 -19
  590. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +421 -19
  591. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +421 -19
  592. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +421 -19
  593. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +426 -20
  594. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +424 -22
  595. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +421 -19
  596. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +421 -19
  597. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9182 -0
  598. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +424 -22
  599. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +421 -19
  600. nautobot/project-static/docs/user-guide/platform-functionality/note.html +421 -19
  601. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +421 -19
  602. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +424 -22
  603. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9250 -0
  604. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +421 -19
  605. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +421 -19
  606. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +424 -22
  607. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +421 -19
  608. nautobot/project-static/docs/user-guide/platform-functionality/role.html +421 -19
  609. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +421 -19
  610. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +421 -19
  611. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +424 -22
  612. nautobot/project-static/docs/user-guide/platform-functionality/status.html +421 -19
  613. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +421 -19
  614. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +489 -56
  615. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +421 -19
  616. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +421 -19
  617. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +421 -19
  618. nautobot/project-static/img/jinja_logo.svg +97 -0
  619. nautobot/project-static/js/forms.js +5 -0
  620. nautobot/project-static/js/nav_menu.js +2 -1
  621. nautobot/tenancy/api/serializers.py +0 -2
  622. nautobot/tenancy/factory.py +1 -1
  623. nautobot/tenancy/navigation.py +0 -29
  624. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  625. nautobot/tenancy/tests/test_filters.py +29 -134
  626. nautobot/tenancy/views.py +32 -23
  627. nautobot/users/admin.py +3 -1
  628. nautobot/users/api/serializers.py +4 -5
  629. nautobot/users/api/views.py +1 -1
  630. nautobot/users/forms.py +19 -0
  631. nautobot/users/templates/users/preferences.html +22 -0
  632. nautobot/users/tests/test_filters.py +1 -19
  633. nautobot/users/tests/test_views.py +57 -0
  634. nautobot/users/utils.py +8 -0
  635. nautobot/users/views.py +48 -11
  636. nautobot/virtualization/api/serializers.py +4 -4
  637. nautobot/virtualization/filters.py +2 -20
  638. nautobot/virtualization/navigation.py +0 -48
  639. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  640. nautobot/virtualization/tests/test_filters.py +57 -183
  641. nautobot/virtualization/views.py +18 -15
  642. nautobot/wireless/__init__.py +0 -0
  643. nautobot/wireless/api/__init__.py +0 -0
  644. nautobot/wireless/api/serializers.py +44 -0
  645. nautobot/wireless/api/urls.py +20 -0
  646. nautobot/wireless/api/views.py +34 -0
  647. nautobot/wireless/apps.py +8 -0
  648. nautobot/wireless/choices.py +345 -0
  649. nautobot/wireless/factory.py +138 -0
  650. nautobot/wireless/filters.py +167 -0
  651. nautobot/wireless/forms.py +283 -0
  652. nautobot/wireless/homepage.py +19 -0
  653. nautobot/wireless/migrations/0001_initial.py +223 -0
  654. nautobot/wireless/migrations/__init__.py +0 -0
  655. nautobot/wireless/models.py +207 -0
  656. nautobot/wireless/navigation.py +105 -0
  657. nautobot/wireless/tables.py +244 -0
  658. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  659. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  660. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  661. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +64 -0
  662. nautobot/wireless/tests/__init__.py +0 -0
  663. nautobot/wireless/tests/test_api.py +247 -0
  664. nautobot/wireless/tests/test_filters.py +54 -0
  665. nautobot/wireless/tests/test_models.py +22 -0
  666. nautobot/wireless/tests/test_views.py +378 -0
  667. nautobot/wireless/urls.py +13 -0
  668. nautobot/wireless/views.py +129 -0
  669. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +11 -14
  670. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +674 -551
  671. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
  672. nautobot/core/utils/navigation.py +0 -54
  673. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
  674. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
  675. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
nautobot/extras/jobs.py CHANGED
@@ -30,25 +30,32 @@ import netaddr
30
30
  import yaml
31
31
 
32
32
  from nautobot.core.celery import import_jobs, nautobot_task
33
+ from nautobot.core.events import publish_event
33
34
  from nautobot.core.forms import (
34
35
  DynamicModelChoiceField,
35
36
  DynamicModelMultipleChoiceField,
36
37
  JSONField,
37
38
  )
38
39
  from nautobot.core.utils.config import get_settings_or_config
40
+ from nautobot.core.utils.logging import sanitize
39
41
  from nautobot.core.utils.lookup import get_model_from_name
40
- from nautobot.extras.choices import JobResultStatusChoices, ObjectChangeActionChoices, ObjectChangeEventContextChoices
42
+ from nautobot.extras.choices import (
43
+ JobResultStatusChoices,
44
+ ObjectChangeActionChoices,
45
+ ObjectChangeEventContextChoices,
46
+ )
41
47
  from nautobot.extras.context_managers import web_request_context
42
48
  from nautobot.extras.forms import JobForm
43
49
  from nautobot.extras.models import (
44
50
  FileProxy,
45
51
  Job as JobModel,
46
52
  JobHook,
53
+ JobQueue,
47
54
  JobResult,
48
55
  ObjectChange,
49
56
  )
50
57
  from nautobot.extras.registry import registry
51
- from nautobot.extras.utils import change_logged_models_queryset, task_queues_as_choices
58
+ from nautobot.extras.utils import change_logged_models_queryset
52
59
  from nautobot.ipam.formfields import IPAddressFormField, IPNetworkFormField
53
60
  from nautobot.ipam.validators import (
54
61
  MaxPrefixLengthValidator,
@@ -98,15 +105,13 @@ class BaseJob:
98
105
 
99
106
  - name (str)
100
107
  - description (str)
101
- - approval_required (bool)
102
- - dryrun_default (bool)
103
- - field_order (list)
104
- - has_sensitive_variables (bool)
105
108
  - hidden (bool)
109
+ - field_order (list)
110
+ - approval_required (bool)
106
111
  - soft_time_limit (int)
107
- - task_queues (list)
108
- - template_name (str)
109
112
  - time_limit (int)
113
+ - has_sensitive_variables (bool)
114
+ - task_queues (list)
110
115
  """
111
116
 
112
117
  def __init__(self):
@@ -275,7 +280,7 @@ class BaseJob:
275
280
  self._delete_file_proxies(*file_ids)
276
281
 
277
282
  if status == JobResultStatusChoices.STATUS_SUCCESS:
278
- self.logger.info("Job completed", extra={"grouping": "post_run"})
283
+ self.logger.success("Job completed", extra={"grouping": "post_run"})
279
284
 
280
285
  @final
281
286
  @classproperty
@@ -295,7 +300,7 @@ class BaseJob:
295
300
  - my_plugin.jobs.MyPluginJob - App-provided Job
296
301
  - git_repository.jobs.myjob.MyJob - GitRepository Job
297
302
  """
298
- return f"{cls.__module__}.{cls.__name__}" # pylint: disable=no-member
303
+ return f"{cls.__module__}.{cls.__name__}"
299
304
 
300
305
  @final
301
306
  @classproperty
@@ -332,7 +337,7 @@ class BaseJob:
332
337
  @final
333
338
  @classproperty
334
339
  def name(cls) -> str: # pylint: disable=no-self-argument
335
- return cls._get_meta_attr_and_assert_type("name", cls.__name__, expected_type=str) # pylint: disable=no-member
340
+ return cls._get_meta_attr_and_assert_type("name", cls.__name__, expected_type=str)
336
341
 
337
342
  @final
338
343
  @classproperty
@@ -420,7 +425,7 @@ class BaseJob:
420
425
  @classproperty
421
426
  def registered_name(cls) -> str: # pylint: disable=no-self-argument
422
427
  """Deprecated - use class_path classproperty instead."""
423
- return f"{cls.__module__}.{cls.__name__}" # pylint: disable=no-member
428
+ return f"{cls.__module__}.{cls.__name__}"
424
429
 
425
430
  @classmethod
426
431
  def _get_vars(cls):
@@ -475,18 +480,28 @@ class BaseJob:
475
480
  """
476
481
 
477
482
  form = cls.as_form_class()(data, files, initial=initial)
483
+ form.fields["_profile"] = forms.BooleanField(
484
+ required=False,
485
+ initial=False,
486
+ label="Profile job execution",
487
+ help_text="Profiles the job execution using cProfile and outputs a report to /tmp/",
488
+ )
478
489
 
479
- try:
480
- job_model = JobModel.objects.get_for_class_path(cls.class_path)
481
- dryrun_default = job_model.dryrun_default if job_model.dryrun_default_override else cls.dryrun_default
482
- task_queues = job_model.task_queues if job_model.task_queues_override else cls.task_queues
483
- except JobModel.DoesNotExist:
484
- logger.error("No Job instance found in the database corresponding to %s", cls.class_path)
485
- dryrun_default = cls.dryrun_default
486
- task_queues = cls.task_queues
487
-
488
- # Update task queue choices
489
- form.fields["_task_queue"].choices = task_queues_as_choices(task_queues)
490
+ job_model = JobModel.objects.get_for_class_path(cls.class_path)
491
+ dryrun_default = job_model.dryrun_default if job_model.dryrun_default_override else cls.dryrun_default
492
+ job_queue_queryset = JobQueue.objects.filter(jobs=job_model)
493
+ job_queue_params = {"jobs": [job_model.pk]}
494
+
495
+ # Initialize job_queue choices
496
+ form.fields["_job_queue"] = DynamicModelChoiceField(
497
+ queryset=job_queue_queryset,
498
+ query_params=job_queue_params,
499
+ required=False,
500
+ help_text="The job queue to route this job to",
501
+ label="Job queue",
502
+ )
503
+ # Populate the job queue field on the JobRun Form
504
+ form.fields["_job_queue"].initial = job_model.default_job_queue.pk
490
505
 
491
506
  if cls.supports_dryrun and (not initial or "dryrun" not in initial):
492
507
  # Set initial "dryrun" checkbox state based on the Meta parameter
@@ -503,6 +518,11 @@ class BaseJob:
503
518
  for _, field in form.fields.items():
504
519
  field.disabled = True
505
520
 
521
+ # Ensure non-Job-specific fields are still last after applying field_order
522
+ for field in ["_job_queue", "_profile"]:
523
+ value = form.fields.pop(field)
524
+ form.fields[field] = value
525
+
506
526
  return form
507
527
 
508
528
  @functools.cached_property
@@ -721,7 +741,7 @@ class BaseJob:
721
741
  """
722
742
  if isinstance(content, str):
723
743
  content = content.encode("utf-8")
724
- max_size = get_settings_or_config("JOB_CREATE_FILE_MAX_SIZE")
744
+ max_size = get_settings_or_config("JOB_CREATE_FILE_MAX_SIZE", fallback=10 << 20)
725
745
  actual_size = len(content)
726
746
  if actual_size > max_size:
727
747
  raise ValueError(f"Provided {actual_size} bytes of content, but JOB_CREATE_FILE_MAX_SIZE is {max_size}")
@@ -1011,7 +1031,7 @@ class JobHookReceiver(Job):
1011
1031
 
1012
1032
  object_change = ObjectVar(model=ObjectChange)
1013
1033
 
1014
- def run(self, object_change): # pylint: disable=arguments-differ
1034
+ def run(self, object_change):
1015
1035
  """JobHookReceiver subclasses generally shouldn't need to override this method."""
1016
1036
  self.receive_job_hook(
1017
1037
  change=object_change,
@@ -1040,7 +1060,7 @@ class JobButtonReceiver(Job):
1040
1060
  object_pk = StringVar()
1041
1061
  object_model_name = StringVar()
1042
1062
 
1043
- def run(self, object_pk, object_model_name): # pylint: disable=arguments-differ
1063
+ def run(self, object_pk, object_model_name):
1044
1064
  """JobButtonReceiver subclasses generally shouldn't need to override this method."""
1045
1065
  model = get_model_from_name(object_model_name)
1046
1066
  obj = model.objects.get(pk=object_pk)
@@ -1133,16 +1153,32 @@ def run_job(self, job_class_path, *args, **kwargs):
1133
1153
  raise KeyError(f"Job class not found for class path {job_class_path}")
1134
1154
  job = job_class()
1135
1155
  job.request = self.request
1156
+ job_result = JobResult.objects.get(id=self.request.id)
1157
+ payload = {
1158
+ "job_result_id": self.request.id,
1159
+ "job_name": job.name,
1160
+ "user_name": job_result.user.username,
1161
+ }
1162
+ if not job.job_model.has_sensitive_variables:
1163
+ payload["job_kwargs"] = kwargs
1136
1164
  try:
1165
+ publish_event(topic="nautobot.jobs.job.started", payload=payload)
1137
1166
  job.before_start(self.request.id, args, kwargs)
1138
1167
  result = job(*args, **kwargs)
1139
1168
  job.on_success(result, self.request.id, args, kwargs)
1140
1169
  job.after_return(JobResultStatusChoices.STATUS_SUCCESS, result, self.request.id, args, kwargs, None)
1170
+ payload["job_output"] = result
1171
+ publish_event(topic="nautobot.jobs.job.completed", payload=payload)
1141
1172
  return result
1142
1173
  except Exception as exc:
1143
1174
  einfo = ExceptionInfo(sys.exc_info())
1144
1175
  job.on_failure(exc, self.request.id, args, kwargs, einfo)
1145
1176
  job.after_return(JobResultStatusChoices.STATUS_FAILURE, exc, self.request.id, args, kwargs, einfo)
1177
+ payload["einfo"] = {
1178
+ "exc_type": type(exc).__name__,
1179
+ "exc_message": sanitize(str(exc)),
1180
+ }
1181
+ publish_event(topic="nautobot.jobs.job.completed", payload=payload)
1146
1182
  raise
1147
1183
 
1148
1184
 
@@ -28,6 +28,7 @@ STATUS_CHOICESET_MAP = {
28
28
  "dcim.InterfaceRedundancyGroup": dcim_choices.InterfaceRedundancyGroupStatusChoices,
29
29
  "dcim.SoftwareImageFile": dcim_choices.SoftwareImageFileStatusChoices,
30
30
  "dcim.SoftwareVersion": dcim_choices.SoftwareVersionStatusChoices,
31
+ "dcim.VirtualDeviceContext": dcim_choices.VirtualDeviceContextStatusChoices,
31
32
  "extras.ContactAssociation": extras_choices.ContactAssociationStatusChoices,
32
33
  "ipam.IPAddress": ipam_choices.IPAddressStatusChoices,
33
34
  "ipam.Prefix": ipam_choices.PrefixStatusChoices,
@@ -1,14 +1,11 @@
1
- import json
2
1
  import time
3
2
 
4
3
  from django.contrib.auth import get_user_model
5
4
  from django.core.management.base import BaseCommand, CommandError
6
- from django.utils import timezone
7
5
 
8
- from nautobot.extras.choices import JobResultStatusChoices, LogLevelChoices
9
- from nautobot.extras.jobs import get_job
10
- from nautobot.extras.models import Job, JobLogEntry, JobResult
11
- from nautobot.extras.utils import get_worker_count
6
+ from nautobot.extras.choices import JobResultStatusChoices
7
+ from nautobot.extras.management.utils import report_job_status, validate_job_and_job_data
8
+ from nautobot.extras.models import Job, JobResult
12
9
 
13
10
 
14
11
  class Command(BaseCommand):
@@ -38,42 +35,15 @@ class Command(BaseCommand):
38
35
  def handle(self, *args, **options):
39
36
  if "." not in options["job"]:
40
37
  raise CommandError('Job must be specified in the Python module form: "<module_name>.<JobClassName>"')
41
- job_class = get_job(options["job"])
42
- if not job_class:
43
- raise CommandError(f'Job "{options["job"]}" not found')
44
-
45
38
  User = get_user_model()
46
39
  try:
47
40
  user = User.objects.get(username=options["username"])
48
41
  except User.DoesNotExist as exc:
49
42
  raise CommandError("No such user") from exc
50
43
 
51
- data = {}
52
- try:
53
- if options.get("data"):
54
- data = json.loads(options["data"])
55
- except json.decoder.JSONDecodeError as error:
56
- raise CommandError(f"Invalid JSON data:\n{error!s}") from error
57
-
58
- try:
59
- job_model = Job.objects.get_for_class_path(options["job"])
60
- except Job.DoesNotExist as error:
61
- raise CommandError(f"Job {options['job']} does not exist.") from error
62
-
63
- try:
64
- job_model = Job.objects.restrict(user, "run").get_for_class_path(options["job"])
65
- except Job.DoesNotExist:
66
- raise CommandError(f"User {options['username']} does not have permission to run this Job") from None
67
-
68
- if not job_model.installed or job_model.job_class is None:
69
- raise CommandError("Job is not presently installed")
70
- if not job_model.enabled:
71
- raise CommandError("Job is not presently enabled to be run")
72
- if not options["local"] and not get_worker_count():
73
- raise CommandError("No Celery worker detected. Perhaps you meant to specify --local?")
74
-
75
- # Run the job and create a new JobResult
76
- self.stdout.write(f"[{timezone.now():%H:%M:%S}] Running {options['job']}...")
44
+ job_class_path = options["job"]
45
+ data = validate_job_and_job_data(self, user, job_class_path, options.get("data"))
46
+ job_model = Job.objects.get_for_class_path(job_class_path)
77
47
 
78
48
  if options["local"]:
79
49
  job_result = JobResult.execute_job(job_model, user, profile=options["profile"], **data)
@@ -86,46 +56,4 @@ class Command(BaseCommand):
86
56
  job_result.refresh_from_db()
87
57
 
88
58
  # Report on success/failure
89
- groups = set(JobLogEntry.objects.filter(job_result=job_result).values_list("grouping", flat=True))
90
- for group in sorted(groups):
91
- logs = JobLogEntry.objects.filter(job_result__pk=job_result.pk, grouping=group)
92
- debug_count = logs.filter(log_level=LogLevelChoices.LOG_DEBUG).count()
93
- info_count = logs.filter(log_level=LogLevelChoices.LOG_INFO).count()
94
- warning_count = logs.filter(log_level=LogLevelChoices.LOG_WARNING).count()
95
- error_count = logs.filter(log_level=LogLevelChoices.LOG_ERROR).count()
96
- critical_count = logs.filter(log_level=LogLevelChoices.LOG_CRITICAL).count()
97
-
98
- self.stdout.write(
99
- f"\t{group}: {debug_count} debug, {info_count} info, {warning_count} warning, {error_count} error, {critical_count} critical"
100
- )
101
-
102
- for log_entry in logs:
103
- status = log_entry.log_level
104
- if status == "success":
105
- status = self.style.SUCCESS(status)
106
- elif status == "info":
107
- status = status
108
- elif status == "warning":
109
- status = self.style.WARNING(status)
110
- elif status == "failure":
111
- status = self.style.NOTICE(status)
112
-
113
- if log_entry.log_object:
114
- self.stdout.write(f"\t\t{status}: {log_entry.log_object}: {log_entry.message}")
115
- else:
116
- self.stdout.write(f"\t\t{status}: {log_entry.message}")
117
-
118
- if job_result.result:
119
- self.stdout.write(str(job_result.result))
120
-
121
- if job_result.status == JobResultStatusChoices.STATUS_FAILURE:
122
- status = self.style.ERROR("FAILURE")
123
- elif job_result.status == JobResultStatusChoices.STATUS_SUCCESS:
124
- status = self.style.SUCCESS("SUCCESS")
125
- else:
126
- status = self.style.WARNING(job_result.status)
127
- self.stdout.write(f"[{timezone.now():%H:%M:%S}] {options['job']}: {status}")
128
-
129
- # Wrap things up
130
- self.stdout.write(f"[{timezone.now():%H:%M:%S}] {options['job']}: Duration {job_result.duration}")
131
- self.stdout.write(f"[{timezone.now():%H:%M:%S}] Finished")
59
+ report_job_status(self, job_result)
@@ -0,0 +1,46 @@
1
+ from django.core.management.base import BaseCommand, CommandError
2
+
3
+ from nautobot.extras.choices import JobResultStatusChoices
4
+ from nautobot.extras.management.utils import report_job_status, validate_job_and_job_data
5
+ from nautobot.extras.models import JobResult
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "Run a job (script, report) to validate or update data in Nautobot"
10
+
11
+ def add_arguments(self, parser):
12
+ parser.add_argument(
13
+ "job_result",
14
+ help="Pass in an existing job result id from the database to continue executing the job on a local system",
15
+ )
16
+ parser.add_argument(
17
+ "--profile",
18
+ action="store_true",
19
+ help="Run cProfile on the job execution and write the result to the disk under /tmp.",
20
+ )
21
+
22
+ def handle(self, *args, **options):
23
+ job_result = None
24
+ job_result_id = options["job_result"]
25
+ try:
26
+ job_result = JobResult.objects.get(pk=job_result_id)
27
+ except JobResult.DoesNotExist:
28
+ raise CommandError(f"Job result with pk {job_result_id} not found.")
29
+ if job_result.status != JobResultStatusChoices.STATUS_PENDING:
30
+ raise CommandError(
31
+ f"Job result has an invalid status {job_result.status} for this command."
32
+ f" You can only pass in a job result with status {JobResultStatusChoices.STATUS_PENDING}"
33
+ )
34
+
35
+ job_user = job_result.user
36
+ job_model = job_result.job_model
37
+ job_class_path = job_model.class_path
38
+
39
+ data = validate_job_and_job_data(self, job_user, job_class_path, job_result.task_kwargs)
40
+
41
+ # execute_job here implies "--local"
42
+ job_result = JobResult.execute_job(
43
+ job_model, job_user, profile=options["profile"], job_result=job_result, **data
44
+ )
45
+ # Report on success/failure
46
+ report_job_status(self, job_result)
@@ -0,0 +1,87 @@
1
+ import json
2
+
3
+ from django.core.management.base import CommandError
4
+ from django.utils import timezone
5
+
6
+ from nautobot.extras.choices import JobResultStatusChoices, LogLevelChoices
7
+ from nautobot.extras.jobs import get_job
8
+ from nautobot.extras.models import Job, JobLogEntry
9
+
10
+
11
+ def validate_job_and_job_data(command, user, job_class_path, data=None):
12
+ job_data = {}
13
+ try:
14
+ if data:
15
+ job_data = json.loads(data)
16
+ except json.decoder.JSONDecodeError as error:
17
+ raise CommandError(f"Invalid JSON data:\n{error!s}") from error
18
+
19
+ if not get_job(job_class_path, reload=True):
20
+ raise CommandError(f'Job "{job_class_path}" not found')
21
+
22
+ try:
23
+ job_model = Job.objects.get_for_class_path(job_class_path)
24
+ except Job.DoesNotExist as error:
25
+ raise CommandError(f"Job {job_class_path} does not exist.") from error
26
+
27
+ try:
28
+ job_model = Job.objects.restrict(user, "run").get_for_class_path(job_class_path)
29
+ except Job.DoesNotExist:
30
+ raise CommandError(f"User {user.username} does not have permission to run this Job") from None
31
+
32
+ if not job_model.installed or job_model.job_class is None:
33
+ raise CommandError("Job is not presently installed")
34
+ if not job_model.enabled:
35
+ raise CommandError("Job is not presently enabled to be run")
36
+
37
+ # Run the job and create a new JobResult
38
+ command.stdout.write(f"[{timezone.now():%H:%M:%S}] Running {job_class_path}...")
39
+ return job_data
40
+
41
+
42
+ def report_job_status(command, job_result):
43
+ # Report on success/failure
44
+ job_class_path = job_result.job_model.class_path
45
+ groups = set(JobLogEntry.objects.filter(job_result=job_result).values_list("grouping", flat=True))
46
+ for group in sorted(groups):
47
+ logs = JobLogEntry.objects.filter(job_result__pk=job_result.pk, grouping=group)
48
+ debug_count = logs.filter(log_level=LogLevelChoices.LOG_DEBUG).count()
49
+ info_count = logs.filter(log_level=LogLevelChoices.LOG_INFO).count()
50
+ warning_count = logs.filter(log_level=LogLevelChoices.LOG_WARNING).count()
51
+ error_count = logs.filter(log_level=LogLevelChoices.LOG_ERROR).count()
52
+ critical_count = logs.filter(log_level=LogLevelChoices.LOG_CRITICAL).count()
53
+
54
+ command.stdout.write(
55
+ f"\t{group}: {debug_count} debug, {info_count} info, {warning_count} warning, {error_count} error, {critical_count} critical"
56
+ )
57
+
58
+ for log_entry in logs:
59
+ status = log_entry.log_level
60
+ if status == "success":
61
+ status = command.style.SUCCESS(status)
62
+ elif status == "info":
63
+ status = status
64
+ elif status == "warning":
65
+ status = command.style.WARNING(status)
66
+ elif status == "failure":
67
+ status = command.style.NOTICE(status)
68
+
69
+ if log_entry.log_object:
70
+ command.stdout.write(f"\t\t{status}: {log_entry.log_object}: {log_entry.message}")
71
+ else:
72
+ command.stdout.write(f"\t\t{status}: {log_entry.message}")
73
+
74
+ if job_result.result:
75
+ command.stdout.write(str(job_result.result))
76
+
77
+ if job_result.status == JobResultStatusChoices.STATUS_FAILURE:
78
+ status = command.style.ERROR("FAILURE")
79
+ elif job_result.status == JobResultStatusChoices.STATUS_SUCCESS:
80
+ status = command.style.SUCCESS("SUCCESS")
81
+ else:
82
+ status = command.style.WARNING(job_result.status)
83
+ command.stdout.write(f"[{timezone.now():%H:%M:%S}] {job_class_path}: {status}")
84
+
85
+ # Wrap things up
86
+ command.stdout.write(f"[{timezone.now():%H:%M:%S}] {job_class_path}: Duration {job_result.duration}")
87
+ command.stdout.write(f"[{timezone.now():%H:%M:%S}] Finished")
@@ -22,9 +22,8 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
22
22
  return self.model(id=task_id)
23
23
 
24
24
  @transaction_retry(max_retries=2)
25
- def store_result( # pylint:disable=arguments-differ # Nautobot adds kwargs like job_model_id and scheduled_job_id
25
+ def store_result(
26
26
  self,
27
- *,
28
27
  task_id,
29
28
  result,
30
29
  status,
@@ -42,7 +41,6 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
42
41
  using=None,
43
42
  content_type=None,
44
43
  content_encoding=None,
45
- **kwargs,
46
44
  ):
47
45
  """
48
46
  Store the result and status of a Celery task.
@@ -2,6 +2,7 @@ from collections import OrderedDict
2
2
 
3
3
  from django.db import migrations
4
4
 
5
+ from nautobot.extras.choices import LogLevelChoices
5
6
  from nautobot.extras.constants import (
6
7
  JOB_LOG_MAX_ABSOLUTE_URL_LENGTH,
7
8
  JOB_LOG_MAX_GROUPING_LENGTH,
@@ -122,13 +123,14 @@ def reverse_migrate_params(apps, schema_editor):
122
123
  ]
123
124
  )
124
125
 
125
- job_result.data[entry.grouping].setdefault(entry.log_level, 0)
126
- job_result.data[entry.grouping][entry.log_level] += 1
127
- if "total" not in job_result.data:
128
- job_result.data["total"] = _data_grouping_struct()
129
- del job_result.data["total"]["log"]
130
- job_result.data["total"].setdefault(entry.log_level, 0)
131
- job_result.data["total"][entry.log_level] += 1
126
+ if entry.log_level != LogLevelChoices.LOG_DEFAULT:
127
+ job_result.data[entry.grouping].setdefault(entry.log_level, 0)
128
+ job_result.data[entry.grouping][entry.log_level] += 1
129
+ if "total" not in job_result.data:
130
+ job_result.data["total"] = _data_grouping_struct()
131
+ del job_result.data["total"]["log"]
132
+ job_result.data["total"].setdefault(entry.log_level, 0)
133
+ job_result.data["total"][entry.log_level] += 1
132
134
 
133
135
  job_result.save()
134
136
 
@@ -0,0 +1,129 @@
1
+ # Generated by Django 4.2.15 on 2024-08-13 20:36
2
+
3
+ import uuid
4
+
5
+ import django.core.serializers.json
6
+ from django.db import migrations, models
7
+ import django.db.models.deletion
8
+
9
+ import nautobot.core.models.fields
10
+ import nautobot.extras.models.mixins
11
+
12
+
13
+ class Migration(migrations.Migration):
14
+ dependencies = [
15
+ ("tenancy", "0009_update_all_charfields_max_length_to_255"),
16
+ ("extras", "0116_fix_dynamic_group_group_type_data_migration"),
17
+ ]
18
+
19
+ operations = [
20
+ migrations.CreateModel(
21
+ name="JobQueue",
22
+ fields=[
23
+ (
24
+ "id",
25
+ models.UUIDField(
26
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True
27
+ ),
28
+ ),
29
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
30
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
31
+ (
32
+ "_custom_field_data",
33
+ models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
34
+ ),
35
+ ("name", models.CharField(max_length=255, unique=True)),
36
+ ("description", models.CharField(blank=True, max_length=255)),
37
+ ("queue_type", models.CharField(max_length=50)),
38
+ ("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
39
+ (
40
+ "tenant",
41
+ models.ForeignKey(
42
+ blank=True,
43
+ null=True,
44
+ on_delete=django.db.models.deletion.PROTECT,
45
+ related_name="job_queues",
46
+ to="tenancy.tenant",
47
+ ),
48
+ ),
49
+ ],
50
+ options={
51
+ "ordering": ["name"],
52
+ },
53
+ bases=(
54
+ nautobot.extras.models.mixins.DynamicGroupMixin,
55
+ nautobot.extras.models.mixins.NotesMixin,
56
+ models.Model,
57
+ ),
58
+ ),
59
+ migrations.CreateModel(
60
+ name="JobQueueAssignment",
61
+ fields=[
62
+ (
63
+ "id",
64
+ models.UUIDField(
65
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True
66
+ ),
67
+ ),
68
+ (
69
+ "job",
70
+ models.ForeignKey(
71
+ on_delete=django.db.models.deletion.CASCADE,
72
+ related_name="job_queue_assignments",
73
+ to="extras.job",
74
+ ),
75
+ ),
76
+ (
77
+ "job_queue",
78
+ models.ForeignKey(
79
+ on_delete=django.db.models.deletion.CASCADE,
80
+ related_name="job_assignments",
81
+ to="extras.jobqueue",
82
+ ),
83
+ ),
84
+ ],
85
+ options={
86
+ "ordering": ["job", "job_queue"],
87
+ "unique_together": {("job", "job_queue")},
88
+ },
89
+ ),
90
+ migrations.AddField(
91
+ model_name="job",
92
+ name="job_queues",
93
+ field=models.ManyToManyField(
94
+ through="extras.JobQueueAssignment", to="extras.jobqueue", related_name="jobs"
95
+ ),
96
+ ),
97
+ migrations.AddField(
98
+ model_name="job",
99
+ name="default_job_queue_override",
100
+ field=models.BooleanField(default=False),
101
+ ),
102
+ migrations.AddField(
103
+ model_name="job",
104
+ name="default_job_queue",
105
+ field=models.ForeignKey(
106
+ blank=True,
107
+ null=True,
108
+ on_delete=django.db.models.deletion.SET_NULL,
109
+ to="extras.jobqueue",
110
+ related_name="default_for_jobs",
111
+ ),
112
+ ),
113
+ migrations.RenameField(
114
+ model_name="job",
115
+ old_name="task_queues_override",
116
+ new_name="job_queues_override",
117
+ ),
118
+ migrations.AddField(
119
+ model_name="scheduledjob",
120
+ name="job_queue",
121
+ field=models.ForeignKey(
122
+ blank=True,
123
+ null=True,
124
+ on_delete=django.db.models.deletion.SET_NULL,
125
+ related_name="scheduled_jobs",
126
+ to="extras.jobqueue",
127
+ ),
128
+ ),
129
+ ]