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
nautobot/extras/utils.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import collections
2
2
  import contextlib
3
+ import copy
3
4
  import hashlib
4
5
  import hmac
5
6
  import logging
@@ -15,13 +16,15 @@ from django.db import transaction
15
16
  from django.db.models import Q
16
17
  from django.template.loader import get_template, TemplateDoesNotExist
17
18
  from django.utils.deconstruct import deconstructible
19
+ import kubernetes.client
18
20
  import redis.exceptions
19
21
 
20
22
  from nautobot.core.choices import ColorChoices
21
23
  from nautobot.core.constants import CHARFIELD_MAX_LENGTH
22
24
  from nautobot.core.models.managers import TagsManager
23
25
  from nautobot.core.models.utils import find_models_with_matching_fields
24
- from nautobot.extras.choices import DynamicGroupTypeChoices, ObjectChangeActionChoices
26
+ from nautobot.core.utils.data import is_uuid
27
+ from nautobot.extras.choices import DynamicGroupTypeChoices, JobQueueTypeChoices, ObjectChangeActionChoices
25
28
  from nautobot.extras.constants import (
26
29
  CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL,
27
30
  EXTRAS_FEATURES,
@@ -395,15 +398,68 @@ def get_celery_queues():
395
398
 
396
399
 
397
400
  def get_worker_count(request=None, queue=None):
401
+ """
402
+ Return a count of the active Celery workers in a specified queue (Could be a JobQueue instance, instance pk or instance name).
403
+ Defaults to the `CELERY_TASK_DEFAULT_QUEUE` setting.
404
+ """
405
+ from nautobot.extras.models import JobQueue
406
+
407
+ celery_queues = get_celery_queues()
408
+ if isinstance(queue, str):
409
+ if is_uuid(queue):
410
+ try:
411
+ # check if the string passed in is a valid UUID
412
+ queue = JobQueue.objects.get(pk=queue).name
413
+ except JobQueue.DoesNotExist:
414
+ return 0
415
+ else:
416
+ return celery_queues.get(queue, 0)
417
+ elif isinstance(queue, JobQueue):
418
+ queue = queue.name
419
+ else:
420
+ queue = settings.CELERY_TASK_DEFAULT_QUEUE
421
+
422
+ return celery_queues.get(queue, 0)
423
+
424
+
425
+ def get_job_queue_worker_count(request=None, job_queue=None):
398
426
  """
399
427
  Return a count of the active Celery workers in a specified queue. Defaults to the `CELERY_TASK_DEFAULT_QUEUE` setting.
428
+ Same as get_worker_count() method above, but job_queue is an actual JobQueue model instance.
400
429
  """
430
+ # TODO currently this method is only retrieve celery specific queues and their respective worker counts
431
+ # Refactor it to support retrieving kubernetes queues as well.
401
432
  celery_queues = get_celery_queues()
402
- if not queue:
433
+ if not job_queue:
403
434
  queue = settings.CELERY_TASK_DEFAULT_QUEUE
435
+ else:
436
+ queue = job_queue.name
404
437
  return celery_queues.get(queue, 0)
405
438
 
406
439
 
440
+ def get_job_queue(job_queue):
441
+ """
442
+ Search for a JobQueue instance based on the str job_queue.
443
+ If no existing Job Queue not found, return None
444
+ """
445
+ from nautobot.extras.models import JobQueue
446
+
447
+ queue = None
448
+ if is_uuid(job_queue):
449
+ try:
450
+ # check if the string passed in is a valid UUID
451
+ queue = JobQueue.objects.get(pk=job_queue)
452
+ except JobQueue.DoesNotExist:
453
+ queue = None
454
+ else:
455
+ try:
456
+ # check if the string passed in is a valid name
457
+ queue = JobQueue.objects.get(name=job_queue)
458
+ except JobQueue.DoesNotExist:
459
+ queue = None
460
+ return queue
461
+
462
+
407
463
  def task_queues_as_choices(task_queues):
408
464
  """
409
465
  Returns a list of 2-tuples for use in the form field `choices` argument. Appends
@@ -424,13 +480,17 @@ def task_queues_as_choices(task_queues):
424
480
  return choices
425
481
 
426
482
 
427
- def refresh_job_model_from_job_class(job_model_class, job_class):
483
+ def refresh_job_model_from_job_class(job_model_class, job_class, job_queue_class=None):
428
484
  """
429
485
  Create or update a job_model record based on the metadata of the provided job_class.
430
486
 
431
- Note that job_model_class is a parameter (rather than doing a "from nautobot.extras.models import Job") because
487
+ Note that `job_model_class` and `job_queue_class` are parameters rather than local imports because
432
488
  this function may be called from various initialization processes (such as the "nautobot_database_ready" signal)
433
489
  and in that case we need to not import models ourselves.
490
+
491
+ The `job_queue_class` parameter really should be required, but for some reason we decided to make this function
492
+ part of the `nautobot.apps.utils` API surface and so we need it to stay backwards-compatible with Apps that might
493
+ be calling the two-argument form of this function.
434
494
  """
435
495
  from nautobot.extras.jobs import (
436
496
  JobButtonReceiver,
@@ -502,6 +562,10 @@ def refresh_job_model_from_job_class(job_model_class, job_class):
502
562
 
503
563
  try:
504
564
  with transaction.atomic():
565
+ default_job_queue, _ = job_queue_class.objects.get_or_create(
566
+ name=job_class.task_queues[0] if job_class.task_queues else settings.CELERY_TASK_DEFAULT_QUEUE,
567
+ defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY},
568
+ )
505
569
  job_model, created = job_model_class.objects.get_or_create(
506
570
  module_name=job_class.__module__[:JOB_MAX_NAME_LENGTH],
507
571
  job_class_name=job_class.__name__[:JOB_MAX_NAME_LENGTH],
@@ -514,6 +578,8 @@ def refresh_job_model_from_job_class(job_model_class, job_class):
514
578
  "supports_dryrun": job_class.supports_dryrun,
515
579
  "installed": True,
516
580
  "enabled": False,
581
+ "default_job_queue": default_job_queue,
582
+ "is_singleton": job_class.is_singleton,
517
583
  },
518
584
  )
519
585
 
@@ -530,6 +596,19 @@ def refresh_job_model_from_job_class(job_model_class, job_class):
530
596
  # It was inherited and not overridden
531
597
  setattr(job_model, field_name, getattr(job_class, field_name))
532
598
 
599
+ # Special case for backward compatibility
600
+ # Note that the `job_model.task_queues` setter does NOT auto-create Celery JobQueue records;
601
+ # this is a special case where we DO want to do so.
602
+ if job_queue_class is not None and not job_model.job_queues_override:
603
+ job_queues = []
604
+ task_queues = job_class.task_queues or [settings.CELERY_TASK_DEFAULT_QUEUE]
605
+ for task_queue in task_queues:
606
+ job_queue, _ = job_queue_class.objects.get_or_create(
607
+ name=task_queue, defaults={"queue_type": JobQueueTypeChoices.TYPE_CELERY}
608
+ )
609
+ job_queues.append(job_queue)
610
+ job_model.job_queues.set(job_queues)
611
+
533
612
  if not created:
534
613
  # Mark it as installed regardless
535
614
  job_model.installed = True
@@ -558,6 +637,42 @@ def refresh_job_model_from_job_class(job_model_class, job_class):
558
637
  return (job_model, created)
559
638
 
560
639
 
640
+ def run_kubernetes_job_and_return_job_result(job_queue, job_result, job_kwargs):
641
+ """
642
+ Pass the job to a kubernetes pod and execute it there.
643
+ """
644
+ pod_name = settings.KUBERNETES_JOB_POD_NAME
645
+ pod_namespace = settings.KUBERNETES_JOB_POD_NAMESPACE
646
+ pod_manifest = copy.deepcopy(settings.KUBERNETES_JOB_MANIFEST)
647
+ pod_ssl_ca_cert = settings.KUBERNETES_SSL_CA_CERT_PATH
648
+ pod_token = settings.KUBERNETES_TOKEN_PATH
649
+
650
+ configuration = kubernetes.client.Configuration()
651
+ configuration.host = settings.KUBERNETES_DEFAULT_SERVICE_ADDRESS
652
+ configuration.ssl_ca_cert = pod_ssl_ca_cert
653
+ with open(pod_token, "r") as token_file:
654
+ token = token_file.read().strip()
655
+ # configure API Key authorization: BearerToken
656
+ configuration.api_key_prefix["authorization"] = "Bearer"
657
+ configuration.api_key["authorization"] = token
658
+ with kubernetes.client.ApiClient(configuration) as api_client:
659
+ api_instance = kubernetes.client.BatchV1Api(api_client)
660
+
661
+ job_result.task_kwargs = job_kwargs
662
+ job_result.save()
663
+ pod_manifest["metadata"]["name"] = "nautobot-job-" + str(job_result.pk)
664
+ pod_manifest["spec"]["template"]["spec"]["containers"][0]["command"] = [
665
+ "nautobot-server",
666
+ "runjob_with_job_result",
667
+ f"{job_result.pk}",
668
+ ]
669
+ job_result.log(f"Creating job pod {pod_name} in namespace {pod_namespace}")
670
+ api_instance.create_namespaced_job(body=pod_manifest, namespace=pod_namespace)
671
+ job_result.log(f"Reading job pod {pod_name} in namespace {pod_namespace}")
672
+ api_instance.read_namespaced_job(name="nautobot-job-" + str(job_result.pk), namespace=pod_namespace)
673
+ return job_result
674
+
675
+
561
676
  def remove_prefix_from_cf_key(field_name):
562
677
  """
563
678
  field_name (str): f"cf_{cf.key}"
nautobot/extras/views.py CHANGED
@@ -1,8 +1,6 @@
1
1
  import logging
2
2
  from urllib.parse import parse_qs
3
3
 
4
- from celery import chain
5
- from django.conf import settings
6
4
  from django.contrib import messages
7
5
  from django.contrib.contenttypes.models import ContentType
8
6
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
@@ -18,21 +16,21 @@ from django.utils import timezone
18
16
  from django.utils.encoding import iri_to_uri
19
17
  from django.utils.html import format_html
20
18
  from django.utils.http import url_has_allowed_host_and_scheme
19
+ from django.utils.timezone import get_current_timezone
21
20
  from django.views.generic import View
22
21
  from django_tables2 import RequestConfig
23
22
  from jsonschema.validators import Draft7Validator
24
23
  from rest_framework.decorators import action
25
24
  from rest_framework.permissions import IsAuthenticated
26
25
 
27
- try:
28
- from zoneinfo import ZoneInfo
29
- except ImportError: # python 3.8
30
- from backports.zoneinfo import ZoneInfo
31
-
26
+ from nautobot.core.constants import PAGINATE_COUNT_DEFAULT
27
+ from nautobot.core.events import publish_event
32
28
  from nautobot.core.forms import restrict_form_fields
33
29
  from nautobot.core.models.querysets import count_related
34
- from nautobot.core.models.utils import pretty_print_query
30
+ from nautobot.core.models.utils import pretty_print_query, serialize_object_v2
35
31
  from nautobot.core.tables import ButtonsColumn
32
+ from nautobot.core.ui import object_detail
33
+ from nautobot.core.ui.choices import SectionChoices
36
34
  from nautobot.core.utils.config import get_settings_or_config
37
35
  from nautobot.core.utils.lookup import (
38
36
  get_filterset_for_model,
@@ -45,6 +43,7 @@ from nautobot.core.utils.requests import normalize_querydict
45
43
  from nautobot.core.views import generic, viewsets
46
44
  from nautobot.core.views.mixins import (
47
45
  GetReturnURLMixin,
46
+ ObjectBulkCreateViewMixin,
48
47
  ObjectBulkDestroyViewMixin,
49
48
  ObjectBulkUpdateViewMixin,
50
49
  ObjectChangeLogViewMixin,
@@ -52,18 +51,23 @@ from nautobot.core.views.mixins import (
52
51
  ObjectDetailViewMixin,
53
52
  ObjectEditViewMixin,
54
53
  ObjectListViewMixin,
54
+ ObjectNotesViewMixin,
55
55
  ObjectPermissionRequiredMixin,
56
56
  )
57
57
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
58
58
  from nautobot.core.views.utils import prepare_cloned_fields
59
59
  from nautobot.core.views.viewsets import NautobotUIViewSet
60
- from nautobot.dcim.models import Controller, Device, Interface, Module, Rack
61
- from nautobot.dcim.tables import ControllerTable, DeviceTable, InterfaceTable, ModuleTable, RackTable
62
- from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
60
+ from nautobot.dcim.models import Controller, Device, Interface, Module, Rack, VirtualDeviceContext
61
+ from nautobot.dcim.tables import (
62
+ ControllerTable,
63
+ DeviceTable,
64
+ InterfaceTable,
65
+ ModuleTable,
66
+ RackTable,
67
+ VirtualDeviceContextTable,
68
+ )
63
69
  from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
64
- from nautobot.extras.signals import change_context_state
65
- from nautobot.extras.tasks import delete_custom_field_data
66
- from nautobot.extras.utils import get_base_template, get_worker_count
70
+ from nautobot.extras.utils import get_base_template, get_job_queue, get_worker_count
67
71
  from nautobot.ipam.models import IPAddress, Prefix, VLAN
68
72
  from nautobot.ipam.tables import IPAddressTable, PrefixTable, VLANTable
69
73
  from nautobot.virtualization.models import VirtualMachine, VMInterface
@@ -71,7 +75,13 @@ from nautobot.virtualization.tables import VirtualMachineTable, VMInterfaceTable
71
75
 
72
76
  from . import filters, forms, tables
73
77
  from .api import serializers
74
- from .choices import DynamicGroupTypeChoices, JobExecutionType, JobResultStatusChoices, LogLevelChoices
78
+ from .choices import (
79
+ DynamicGroupTypeChoices,
80
+ JobExecutionType,
81
+ JobQueueTypeChoices,
82
+ JobResultStatusChoices,
83
+ LogLevelChoices,
84
+ )
75
85
  from .datasources import (
76
86
  enqueue_git_repository_diff_origin_and_local,
77
87
  enqueue_pull_git_repository_and_refresh_data,
@@ -96,6 +106,7 @@ from .models import (
96
106
  JobButton,
97
107
  JobHook,
98
108
  JobLogEntry,
109
+ JobQueue,
99
110
  JobResult,
100
111
  MetadataType,
101
112
  Note,
@@ -643,37 +654,7 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView):
643
654
  queryset = CustomField.objects.all()
644
655
  table = tables.CustomFieldTable
645
656
  filterset = filters.CustomFieldFilterSet
646
-
647
- def construct_custom_field_delete_tasks(self, queryset):
648
- """
649
- Helper method to construct a list of celery tasks to execute when bulk deleting custom fields.
650
- """
651
- change_context = change_context_state.get()
652
- if change_context is None:
653
- context = None
654
- else:
655
- context = change_context.as_dict(queryset)
656
- context["context_detail"] = "bulk delete custom field data"
657
- tasks = [
658
- delete_custom_field_data.si(obj.key, set(obj.content_types.values_list("pk", flat=True)), context)
659
- for obj in queryset
660
- ]
661
- return tasks
662
-
663
- def perform_pre_delete(self, request, queryset):
664
- """
665
- Remove all Custom Field Keys/Values from _custom_field_data of the related ContentType in the background.
666
- """
667
- if not get_worker_count():
668
- messages.error(
669
- request, "Celery worker process not running. Object custom fields may fail to reflect this deletion."
670
- )
671
- return
672
- tasks = self.construct_custom_field_delete_tasks(queryset)
673
- # Executing the tasks in the background sequentially using chain() aligns with how a single
674
- # CustomField object is deleted. We decided to not check the result because it needs at least one worker
675
- # to be active and comes with extra performance penalty.
676
- chain(*tasks).apply_async()
657
+ form = forms.CustomFieldBulkDeleteForm
677
658
 
678
659
 
679
660
  #
@@ -1006,6 +987,27 @@ class ExternalIntegrationUIViewSet(NautobotUIViewSet):
1006
987
  serializer_class = serializers.ExternalIntegrationSerializer
1007
988
  table_class = tables.ExternalIntegrationTable
1008
989
 
990
+ object_detail_content = object_detail.ObjectDetailContent(
991
+ panels=(
992
+ object_detail.ObjectFieldsPanel(
993
+ section=SectionChoices.LEFT_HALF,
994
+ weight=100,
995
+ # Default ordering with __all__ leaves something to be desired
996
+ fields=[
997
+ "name",
998
+ "remote_url",
999
+ "http_method",
1000
+ "headers",
1001
+ "verify_ssl",
1002
+ "ca_file_path",
1003
+ "secrets_group",
1004
+ "timeout",
1005
+ "extra_config",
1006
+ ],
1007
+ ),
1008
+ ),
1009
+ )
1010
+
1009
1011
 
1010
1012
  #
1011
1013
  # Git repositories
@@ -1319,10 +1321,18 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1319
1321
  # for example "?kwargs_from_job_result=<UUID>&integervar=22"
1320
1322
  explicit_initial = initial
1321
1323
  initial = job_result.task_kwargs.copy()
1322
- task_queue = job_result.celery_kwargs.get("queue", None)
1323
- if task_queue is not None:
1324
- initial["_task_queue"] = task_queue
1324
+ job_queue = job_result.celery_kwargs.get("queue", None)
1325
+ jq = None
1326
+ if job_queue is not None:
1327
+ try:
1328
+ jq = JobQueue.objects.get(name=job_queue, queue_type=JobQueueTypeChoices.TYPE_CELERY)
1329
+ except JobQueue.DoesNotExist:
1330
+ pass
1331
+ initial["_job_queue"] = jq
1325
1332
  initial["_profile"] = job_result.celery_kwargs.get("nautobot_job_profile", False)
1333
+ initial["_ignore_singleton_lock"] = job_result.celery_kwargs.get(
1334
+ "nautobot_job_ignore_singleton_lock", False
1335
+ )
1326
1336
  initial.update(explicit_initial)
1327
1337
  except JobResult.DoesNotExist:
1328
1338
  messages.warning(
@@ -1362,7 +1372,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1362
1372
  job_class = get_job(job_model.class_path, reload=True)
1363
1373
  job_form = job_class.as_form(request.POST, request.FILES) if job_class is not None else None
1364
1374
  schedule_form = forms.JobScheduleForm(request.POST)
1365
- task_queue = request.POST.get("_task_queue")
1375
+ job_queue = request.POST.get("_job_queue")
1366
1376
 
1367
1377
  return_url = request.POST.get("_return_url")
1368
1378
  if return_url is not None and url_has_allowed_host_and_scheme(url=return_url, allowed_hosts=request.get_host()):
@@ -1370,8 +1380,11 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1370
1380
  else:
1371
1381
  return_url = None
1372
1382
 
1373
- # Allow execution only if a worker process is running and the job is runnable.
1374
- if not get_worker_count(queue=task_queue):
1383
+ queue = get_job_queue(job_queue)
1384
+ if queue is None:
1385
+ queue = job_model.default_job_queue
1386
+ # Allow execution only if a worker process is running on a celery queue and the job is runnable.
1387
+ if queue.queue_type == JobQueueTypeChoices.TYPE_CELERY and not get_worker_count(queue=job_queue):
1375
1388
  messages.error(request, "Unable to run or schedule job: Celery worker process not running.")
1376
1389
  elif not job_model.installed or job_class is None:
1377
1390
  messages.error(request, "Unable to run or schedule job: Job is not presently installed.")
@@ -1390,10 +1403,21 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1390
1403
  "One of these two flags must be removed before this job can be scheduled or run.",
1391
1404
  )
1392
1405
  elif job_form is not None and job_form.is_valid() and schedule_form.is_valid():
1393
- task_queue = job_form.cleaned_data.pop("_task_queue", None)
1406
+ job_queue = job_form.cleaned_data.pop("_job_queue", None)
1407
+ jq = None
1408
+ if job_queue is not None:
1409
+ try:
1410
+ jq = JobQueue.objects.get(pk=job_queue)
1411
+ except (ValidationError, JobQueue.DoesNotExist):
1412
+ try:
1413
+ jq = JobQueue.objects.get(name=job_queue)
1414
+ except JobQueue.DoesNotExist:
1415
+ pass
1416
+
1394
1417
  dryrun = job_form.cleaned_data.get("dryrun", False)
1395
1418
  # Run the job. A new JobResult is created.
1396
1419
  profile = job_form.cleaned_data.pop("_profile")
1420
+ ignore_singleton_lock = job_form.cleaned_data.pop("_ignore_singleton_lock", False)
1397
1421
  schedule_type = schedule_form.cleaned_data["_schedule_type"]
1398
1422
 
1399
1423
  if (not dryrun and job_model.approval_required) or schedule_type in JobExecutionType.SCHEDULE_CHOICES:
@@ -1405,8 +1429,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1405
1429
  interval=schedule_type,
1406
1430
  crontab=schedule_form.cleaned_data.get("_recurrence_custom_time"),
1407
1431
  approval_required=job_model.approval_required,
1408
- task_queue=task_queue,
1432
+ task_queue=jq.name if jq else None,
1409
1433
  profile=profile,
1434
+ ignore_singleton_lock=ignore_singleton_lock,
1410
1435
  **job_class.serialize_data(job_form.cleaned_data),
1411
1436
  )
1412
1437
 
@@ -1424,7 +1449,8 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1424
1449
  job_model,
1425
1450
  request.user,
1426
1451
  profile=profile,
1427
- task_queue=task_queue,
1452
+ ignore_singleton_lock=ignore_singleton_lock,
1453
+ task_queue=jq.name if jq else None,
1428
1454
  **job_class.serialize_data(job_kwargs),
1429
1455
  )
1430
1456
 
@@ -1482,23 +1508,6 @@ class JobBulkEditView(generic.BulkEditView):
1482
1508
  form = forms.JobBulkEditForm
1483
1509
  template_name = "extras/job_bulk_edit.html"
1484
1510
 
1485
- def extra_post_save_action(self, obj, form):
1486
- cleaned_data = form.cleaned_data
1487
-
1488
- # Handle text related fields
1489
- for overridable_field in JOB_OVERRIDABLE_FIELDS:
1490
- override_field = overridable_field + "_override"
1491
- clear_override_field = "clear_" + overridable_field + "_override"
1492
- reset_override = cleaned_data.get(clear_override_field, False)
1493
- override_value = cleaned_data.get(overridable_field)
1494
- if reset_override:
1495
- setattr(obj, override_field, False)
1496
- elif not reset_override and override_value not in [None, ""]:
1497
- setattr(obj, override_field, True)
1498
- setattr(obj, overridable_field, override_value)
1499
-
1500
- obj.validated_save()
1501
-
1502
1511
 
1503
1512
  class JobDeleteView(generic.ObjectDeleteView):
1504
1513
  queryset = JobModel.objects.all()
@@ -1538,7 +1547,7 @@ class JobApprovalRequestView(generic.ObjectView):
1538
1547
  if job_class is not None:
1539
1548
  # Render the form with all fields disabled
1540
1549
  initial = instance.kwargs
1541
- initial["_task_queue"] = instance.queue
1550
+ initial["_job_queue"] = instance.queue
1542
1551
  initial["_profile"] = instance.celery_kwargs.get("profile", False)
1543
1552
  job_form = job_class().as_form(initial=initial, approval_view=True)
1544
1553
  else:
@@ -1597,12 +1606,15 @@ class JobApprovalRequestView(generic.ObjectView):
1597
1606
  messages.error(request, "You do not have permission to deny this request.")
1598
1607
  else:
1599
1608
  # Delete the scheduled_job instance
1609
+ publish_event_payload = {"data": serialize_object_v2(scheduled_job)}
1600
1610
  scheduled_job.delete()
1601
1611
  if request.user == scheduled_job.user:
1602
1612
  messages.error(request, f"Approval request for {scheduled_job.name} was revoked")
1603
1613
  else:
1604
1614
  messages.error(request, f"Approval of {scheduled_job.name} was denied")
1605
1615
 
1616
+ publish_event(topic="nautobot.jobs.approval.denied", payload=publish_event_payload)
1617
+
1606
1618
  return redirect("extras:scheduledjob_approval_queue_list")
1607
1619
 
1608
1620
  elif approve or force_approve:
@@ -1624,6 +1636,9 @@ class JobApprovalRequestView(generic.ObjectView):
1624
1636
  scheduled_job.approved_at = timezone.now()
1625
1637
  scheduled_job.save()
1626
1638
 
1639
+ publish_event_payload = {"data": serialize_object_v2(scheduled_job)}
1640
+ publish_event(topic="nautobot.jobs.approval.approved", payload=publish_event_payload)
1641
+
1627
1642
  messages.success(request, f"{scheduled_job.name} was approved and will now begin execution")
1628
1643
 
1629
1644
  return redirect("extras:scheduledjob_approval_queue_list")
@@ -1638,6 +1653,28 @@ class JobApprovalRequestView(generic.ObjectView):
1638
1653
  )
1639
1654
 
1640
1655
 
1656
+ class JobQueueUIViewSet(NautobotUIViewSet):
1657
+ bulk_update_form_class = forms.JobQueueBulkEditForm
1658
+ filterset_form_class = forms.JobQueueFilterForm
1659
+ queryset = JobQueue.objects.all()
1660
+ form_class = forms.JobQueueForm
1661
+ filterset_class = filters.JobQueueFilterSet
1662
+ serializer_class = serializers.JobQueueSerializer
1663
+ table_class = tables.JobQueueTable
1664
+
1665
+ def get_extra_context(self, request, instance):
1666
+ context = super().get_extra_context(request, instance)
1667
+
1668
+ if self.action == "retrieve":
1669
+ jobs = instance.jobs.restrict(request.user, "view")
1670
+ jobs_table = tables.JobTable(jobs, orderable=False)
1671
+ paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
1672
+ RequestConfig(request, paginate).configure(jobs_table)
1673
+ context["jobs_table"] = jobs_table
1674
+
1675
+ return context
1676
+
1677
+
1641
1678
  #
1642
1679
  # Saved Views
1643
1680
  #
@@ -1837,7 +1874,7 @@ class SavedViewUIViewSet(
1837
1874
  if derived_instance and derived_instance.config.get("pagination_count", None):
1838
1875
  pagination_count = derived_instance.config["pagination_count"]
1839
1876
  else:
1840
- pagination_count = get_settings_or_config("PAGINATE_COUNT")
1877
+ pagination_count = get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
1841
1878
  sv.config["pagination_count"] = int(pagination_count)
1842
1879
  sort_order = param_dict.get("sort", [])
1843
1880
  if not sort_order:
@@ -1931,7 +1968,7 @@ class ScheduledJobView(generic.ObjectView):
1931
1968
  return {
1932
1969
  "labels": labels,
1933
1970
  "job_class_found": (job_class is not None),
1934
- "default_time_zone": ZoneInfo(settings.TIME_ZONE),
1971
+ "default_time_zone": get_current_timezone(),
1935
1972
  **super().get_extra_context(request, instance),
1936
1973
  }
1937
1974
 
@@ -1990,6 +2027,9 @@ def get_annotated_jobresult_queryset():
1990
2027
  debug_log_count=count_related(
1991
2028
  JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
1992
2029
  ),
2030
+ success_log_count=count_related(
2031
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
2032
+ ),
1993
2033
  info_log_count=count_related(
1994
2034
  JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
1995
2035
  ),
@@ -2469,6 +2509,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2469
2509
  module_table.columns.hide("role")
2470
2510
  RequestConfig(request, paginate).configure(module_table)
2471
2511
  context["module_table"] = module_table
2512
+ if ContentType.objects.get_for_model(VirtualDeviceContext) in context["content_types"]:
2513
+ vdcs = instance.virtual_device_contexts.restrict(request.user, "view")
2514
+ vdc_table = VirtualDeviceContextTable(vdcs)
2515
+ vdc_table.columns.hide("role")
2516
+ RequestConfig(request, paginate).configure(vdc_table)
2517
+ context["vdc_table"] = vdc_table
2472
2518
  return context
2473
2519
 
2474
2520
 
@@ -2477,38 +2523,56 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2477
2523
  #
2478
2524
 
2479
2525
 
2480
- class SecretListView(generic.ObjectListView):
2481
- queryset = Secret.objects.all()
2482
- filterset = filters.SecretFilterSet
2483
- filterset_form = forms.SecretFilterForm
2484
- table = tables.SecretTable
2485
-
2486
-
2487
- class SecretView(generic.ObjectView):
2526
+ class SecretUIViewSet(
2527
+ ObjectDetailViewMixin,
2528
+ ObjectListViewMixin,
2529
+ ObjectEditViewMixin,
2530
+ ObjectDestroyViewMixin,
2531
+ ObjectBulkCreateViewMixin, # 3.0 TODO: remove, unused
2532
+ ObjectBulkDestroyViewMixin,
2533
+ # no ObjectBulkUpdateViewMixin here yet
2534
+ ObjectChangeLogViewMixin,
2535
+ ObjectNotesViewMixin,
2536
+ ):
2488
2537
  queryset = Secret.objects.all()
2538
+ form_class = forms.SecretForm
2539
+ filterset_class = filters.SecretFilterSet
2540
+ filterset_form_class = forms.SecretFilterForm
2541
+ table_class = tables.SecretTable
2542
+
2543
+ object_detail_content = object_detail.ObjectDetailContent(
2544
+ panels=[
2545
+ object_detail.ObjectFieldsPanel(
2546
+ weight=100, section=SectionChoices.LEFT_HALF, fields="__all__", exclude_fields=["parameters"]
2547
+ ),
2548
+ object_detail.KeyValueTablePanel(
2549
+ weight=200, section=SectionChoices.LEFT_HALF, label="Parameters", context_data_key="parameters"
2550
+ ),
2551
+ object_detail.ObjectsTablePanel(
2552
+ weight=100,
2553
+ section=SectionChoices.RIGHT_HALF,
2554
+ table_title="Groups containing this secret",
2555
+ table_class=tables.SecretsGroupTable,
2556
+ table_attribute="secrets_groups",
2557
+ footer_content_template_path=None,
2558
+ ),
2559
+ ],
2560
+ extra_buttons=[
2561
+ object_detail.Button(
2562
+ weight=100,
2563
+ label="Check Secret",
2564
+ icon="mdi-test-tube",
2565
+ javascript_template_path="extras/secret_check.js",
2566
+ attributes={"onClick": "checkSecret()"},
2567
+ ),
2568
+ ],
2569
+ )
2489
2570
 
2490
2571
  def get_extra_context(self, request, instance):
2491
- # Determine user's preferred output format
2492
- if request.GET.get("format") in ["json", "yaml"]:
2493
- format_ = request.GET.get("format")
2494
- if request.user.is_authenticated:
2495
- request.user.set_config("extras.configcontext.format", format_, commit=True)
2496
- elif request.user.is_authenticated:
2497
- format_ = request.user.get_config("extras.configcontext.format", "json")
2498
- else:
2499
- format_ = "json"
2500
-
2501
- provider = registry["secrets_providers"].get(instance.provider)
2502
-
2503
- groups = instance.secrets_groups.distinct()
2504
- groups_table = tables.SecretsGroupTable(groups, orderable=False)
2505
-
2506
- return {
2507
- "format": format_,
2508
- "provider_name": provider.name if provider else instance.provider,
2509
- "groups_table": groups_table,
2510
- **super().get_extra_context(request, instance),
2511
- }
2572
+ ctx = super().get_extra_context(request, instance)
2573
+ if self.action == "retrieve":
2574
+ ctx["parameters"] = instance.parameters
2575
+ return ctx
2512
2576
 
2513
2577
 
2514
2578
  class SecretProviderParametersFormView(generic.GenericView):
@@ -2527,27 +2591,6 @@ class SecretProviderParametersFormView(generic.GenericView):
2527
2591
  )
2528
2592
 
2529
2593
 
2530
- class SecretEditView(generic.ObjectEditView):
2531
- queryset = Secret.objects.all()
2532
- model_form = forms.SecretForm
2533
- template_name = "extras/secret_edit.html"
2534
-
2535
-
2536
- class SecretDeleteView(generic.ObjectDeleteView):
2537
- queryset = Secret.objects.all()
2538
-
2539
-
2540
- class SecretBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
2541
- queryset = Secret.objects.all()
2542
- table = tables.SecretTable
2543
-
2544
-
2545
- class SecretBulkDeleteView(generic.BulkDeleteView):
2546
- queryset = Secret.objects.all()
2547
- filterset = filters.SecretFilterSet
2548
- table = tables.SecretTable
2549
-
2550
-
2551
2594
  class SecretsGroupListView(generic.ObjectListView):
2552
2595
  queryset = SecretsGroup.objects.all()
2553
2596
  filterset = filters.SecretsGroupFilterSet