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
@@ -43,7 +43,7 @@ from nautobot.dcim.graphql.types import (
43
43
  RearPortType,
44
44
  )
45
45
  from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipSideChoices
46
- from nautobot.extras.graphql.types import ContactAssociationType, DynamicGroupType, TagType
46
+ from nautobot.extras.graphql.types import ContactAssociationType, DynamicGroupType, JobType, ScheduledJobType, TagType
47
47
  from nautobot.extras.models import ComputedField, CustomField, Relationship
48
48
  from nautobot.extras.registry import registry
49
49
  from nautobot.extras.utils import check_if_key_is_graphql_safe
@@ -71,6 +71,8 @@ registry["graphql_types"]["dcim.rearport"] = RearPortType
71
71
  registry["graphql_types"]["dcim.location"] = LocationType
72
72
  registry["graphql_types"]["extras.contactassociation"] = ContactAssociationType
73
73
  registry["graphql_types"]["extras.dynamicgroup"] = DynamicGroupType
74
+ registry["graphql_types"]["extras.job"] = JobType
75
+ registry["graphql_types"]["extras.scheduledjob"] = ScheduledJobType
74
76
  registry["graphql_types"]["extras.tag"] = TagType
75
77
  registry["graphql_types"]["ipam.ipaddress"] = IPAddressType
76
78
  registry["graphql_types"]["ipam.prefix"] = PrefixType
@@ -4,7 +4,9 @@ from io import BytesIO
4
4
 
5
5
  from django.conf import settings
6
6
  from django.contrib.contenttypes.models import ContentType
7
- from django.core.exceptions import PermissionDenied
7
+ from django.core.exceptions import (
8
+ PermissionDenied,
9
+ )
8
10
  from django.db import transaction
9
11
  from django.http import QueryDict
10
12
  from rest_framework import exceptions as drf_exceptions
@@ -15,6 +17,7 @@ from nautobot.core.api.renderers import NautobotCSVRenderer
15
17
  from nautobot.core.api.utils import get_serializer_for_model
16
18
  from nautobot.core.celery import app, register_jobs
17
19
  from nautobot.core.exceptions import AbortTransaction
20
+ from nautobot.core.jobs.bulk_actions import BulkDeleteObjects, BulkEditObjects
18
21
  from nautobot.core.jobs.cleanup import LogsCleanup
19
22
  from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
20
23
  from nautobot.core.utils.lookup import get_filterset_for_model
@@ -25,7 +28,16 @@ from nautobot.extras.datasources import (
25
28
  refresh_datasource_content,
26
29
  refresh_job_code_from_repository,
27
30
  )
28
- from nautobot.extras.jobs import BooleanVar, ChoiceVar, FileVar, Job, ObjectVar, RunJobTaskFailed, StringVar, TextVar
31
+ from nautobot.extras.jobs import (
32
+ BooleanVar,
33
+ ChoiceVar,
34
+ FileVar,
35
+ Job,
36
+ ObjectVar,
37
+ RunJobTaskFailed,
38
+ StringVar,
39
+ TextVar,
40
+ )
29
41
  from nautobot.extras.models import ExportTemplate, GitRepository
30
42
 
31
43
  name = "System Jobs"
@@ -347,5 +359,14 @@ class ImportObjects(Job):
347
359
  raise RunJobTaskFailed("CSV import not fully successful, see logs")
348
360
 
349
361
 
350
- jobs = [ExportObjectList, GitRepositorySync, GitRepositoryDryRun, ImportObjects, LogsCleanup, RefreshDynamicGroupCaches]
362
+ jobs = [
363
+ BulkDeleteObjects,
364
+ BulkEditObjects,
365
+ ExportObjectList,
366
+ GitRepositorySync,
367
+ GitRepositoryDryRun,
368
+ ImportObjects,
369
+ LogsCleanup,
370
+ RefreshDynamicGroupCaches,
371
+ ]
351
372
  register_jobs(*jobs)
@@ -0,0 +1,248 @@
1
+ from django.contrib.contenttypes.models import ContentType
2
+ from django.core.exceptions import (
3
+ FieldDoesNotExist,
4
+ ObjectDoesNotExist,
5
+ PermissionDenied,
6
+ ValidationError,
7
+ )
8
+ from django.db.models import ManyToManyField, ProtectedError
9
+
10
+ from nautobot.core.forms.utils import restrict_form_fields
11
+ from nautobot.core.utils.lookup import get_filterset_for_model, get_form_for_model
12
+ from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
13
+ from nautobot.extras.jobs import (
14
+ BooleanVar,
15
+ Job,
16
+ JSONVar,
17
+ ObjectVar,
18
+ RunJobTaskFailed,
19
+ )
20
+ from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
21
+
22
+ name = "System Jobs"
23
+
24
+
25
+ class BulkEditObjects(Job):
26
+ """System Job to bulk Edit objects."""
27
+
28
+ content_type = ObjectVar(
29
+ model=ContentType,
30
+ description="Type of objects to update",
31
+ )
32
+ form_data = JSONVar(description="BulkEditForm data")
33
+ edit_all = BooleanVar(description="Bulk Edit all object / all filtered objects", required=False)
34
+ filter_query_params = JSONVar(label="Filter Query Params", required=False)
35
+
36
+ class Meta:
37
+ name = "Bulk Edit Objects"
38
+ description = "Bulk edit objects."
39
+ has_sensitive_variables = False
40
+ soft_time_limit = 1800
41
+ time_limit = 2000
42
+
43
+ def _update_objects(self, model, form, filter_query_params, edit_all, nullified_fields):
44
+ with deferred_change_logging_for_bulk_operation():
45
+ updated_objects_pk = []
46
+ filterset_cls = get_filterset_for_model(model)
47
+
48
+ if filter_query_params and not filterset_cls:
49
+ self.logger.error(f"Filterset not found for {model}")
50
+ raise RunJobTaskFailed(f"Filter query provided but {model} do not have a filterset.")
51
+
52
+ if edit_all:
53
+ if filterset_cls and filter_query_params:
54
+ queryset = filterset_cls(filter_query_params).qs.restrict(self.user, "change")
55
+ else:
56
+ queryset = model.objects.restrict(self.user, "change")
57
+ else:
58
+ queryset = model.objects.restrict(self.user, "change").filter(pk__in=form.cleaned_data["pk"])
59
+
60
+ form_custom_fields = getattr(form, "custom_fields", [])
61
+ form_relationships = getattr(form, "relationships", [])
62
+ standard_fields = [
63
+ field
64
+ for field in form.fields
65
+ if field not in form_custom_fields + form_relationships + ["pk"] + ["object_note"]
66
+ ]
67
+
68
+ self.logger.debug(f"Performing update on {queryset.count()} {model._meta.verbose_name_plural}")
69
+ for obj in queryset.iterator():
70
+ # Update standard fields. If a field is listed in _nullify, delete its value.
71
+ for field_name in standard_fields:
72
+ try:
73
+ model_field = model._meta.get_field(field_name)
74
+ except FieldDoesNotExist:
75
+ # This form field is used to modify a field rather than set its value directly
76
+ model_field = None
77
+
78
+ # Handle nullification
79
+ if nullified_fields:
80
+ if field_name in form.nullable_fields and field_name in nullified_fields:
81
+ if isinstance(model_field, ManyToManyField):
82
+ getattr(obj, field_name).set([])
83
+ else:
84
+ setattr(obj, field_name, None if model_field is not None and model_field.null else "")
85
+
86
+ # ManyToManyFields
87
+ elif isinstance(model_field, ManyToManyField):
88
+ if form.cleaned_data[field_name]:
89
+ getattr(obj, field_name).set(form.cleaned_data[field_name])
90
+ # Normal fields
91
+ elif form.cleaned_data[field_name] not in (None, ""):
92
+ setattr(obj, field_name, form.cleaned_data[field_name])
93
+
94
+ # Update custom fields
95
+ for field_name in form_custom_fields:
96
+ if field_name in form.nullable_fields and field_name in nullified_fields:
97
+ obj.cf[remove_prefix_from_cf_key(field_name)] = None
98
+ elif form.cleaned_data.get(field_name) not in (None, "", []):
99
+ obj.cf[remove_prefix_from_cf_key(field_name)] = form.cleaned_data[field_name]
100
+
101
+ obj.full_clean()
102
+ obj.save()
103
+ updated_objects_pk.append(obj.pk)
104
+ form.post_save(obj)
105
+
106
+ if hasattr(form, "save_relationships") and callable(form.save_relationships):
107
+ # Add/remove relationship associations
108
+ form.save_relationships(instance=obj, nullified_fields=nullified_fields)
109
+
110
+ if hasattr(form, "save_note") and callable(form.save_note):
111
+ form.save_note(instance=obj, user=self.user)
112
+ total_updated_objs = len(updated_objects_pk)
113
+ # Enforce object-level permissions
114
+ if queryset.filter(pk__in=updated_objects_pk).count() != total_updated_objs:
115
+ raise ObjectDoesNotExist
116
+ return total_updated_objs
117
+
118
+ def _process_valid_form(self, model, form, filter_query_params, edit_all, nullified_fields):
119
+ try:
120
+ total_updated_objs = self._update_objects(model, form, filter_query_params, edit_all, nullified_fields)
121
+ msg = f"Updated {total_updated_objs} {model._meta.verbose_name_plural}"
122
+ self.logger.info(msg)
123
+ return msg
124
+ except ValidationError as e:
125
+ self.logger.error(str(e))
126
+ except ObjectDoesNotExist:
127
+ msg = "Object update failed due to object-level permissions violation"
128
+ self.logger.error(msg)
129
+ raise RunJobTaskFailed("Bulk Edit not fully successful, see logs")
130
+
131
+ def run( # pylint: disable=arguments-differ
132
+ self, *, content_type, form_data, edit_all=False, filter_query_params=None
133
+ ):
134
+ if not self.user.has_perm(f"{content_type.app_label}.change_{content_type.model}"):
135
+ self.logger.error('User "%s" does not have permission to update %s objects', self.user, content_type.model)
136
+ raise PermissionDenied("User does not have change permissions on the requested content-type")
137
+
138
+ model = content_type.model_class()
139
+ if model is None:
140
+ self.logger.error(
141
+ 'Could not find the "%s.%s" data model. Perhaps an app is uninstalled?',
142
+ content_type.app_label,
143
+ content_type.model,
144
+ )
145
+ raise RunJobTaskFailed("Model not found")
146
+ try:
147
+ form_cls = get_form_for_model(model, form_prefix="BulkEdit")
148
+ except Exception:
149
+ self.logger.debug(
150
+ 'Could not find the "%s.%s" data bulk edit form. Unable to process Bulk Edit for this model.',
151
+ content_type.app_label,
152
+ content_type.model,
153
+ )
154
+ raise
155
+ form = form_cls(model, form_data, edit_all=edit_all)
156
+ restrict_form_fields(form, self.user)
157
+
158
+ if form.is_valid():
159
+ self.logger.debug("Form validation was successful")
160
+ nullified_fields = form_data.get("_nullify")
161
+ return self._process_valid_form(model, form, filter_query_params, edit_all, nullified_fields)
162
+ else:
163
+ self.logger.error(f"Form validation unsuccessful: {form.errors.as_json()}")
164
+
165
+ raise RunJobTaskFailed("Updating Jobs Failed")
166
+
167
+
168
+ class BulkDeleteObjects(Job):
169
+ """
170
+ System job to bulk delete objects.
171
+ """
172
+
173
+ content_type = ObjectVar(
174
+ model=ContentType,
175
+ description="Type of objects to delete",
176
+ )
177
+ pk_list = JSONVar(description="List of objects pks to delete", required=False)
178
+ delete_all = BooleanVar(description="Delete all (filtered) objects instead of a list of PKs", required=False)
179
+ filter_query_params = JSONVar(label="Filter Query Params", required=False)
180
+
181
+ class Meta:
182
+ name = "Bulk Delete Objects"
183
+ description = "Bulk delete objects."
184
+ has_sensitive_variables = False
185
+ soft_time_limit = 1800
186
+ time_limit = 2000
187
+
188
+ def run( # pylint: disable=arguments-differ
189
+ self, *, content_type, pk_list=None, delete_all=False, filter_query_params=None
190
+ ):
191
+ if not self.user.has_perm(f"{content_type.app_label}.delete_{content_type.model}"):
192
+ self.logger.error('User "%s" does not have permission to delete %s objects', self.user, content_type.model)
193
+ raise PermissionDenied("User does not have delete permissions on the requested content-type")
194
+
195
+ model = content_type.model_class()
196
+ if model is None:
197
+ self.logger.error(
198
+ 'Could not find the "%s.%s" data model. Perhaps an app is uninstalled?',
199
+ content_type.app_label,
200
+ content_type.model,
201
+ )
202
+ raise RunJobTaskFailed("Model not found")
203
+
204
+ if pk_list and (delete_all or filter_query_params):
205
+ self.logger.error(
206
+ "You can either delete objs within `pk_list` provided or `delete_all` with `filter_query_params` if needed."
207
+ )
208
+ raise RunJobTaskFailed("Both `pk_list` and `delete_all` can not both be set.")
209
+
210
+ filterset_cls = get_filterset_for_model(model)
211
+
212
+ if filter_query_params and not filterset_cls:
213
+ self.logger.error(f"Filterset not found for {model}")
214
+ raise RunJobTaskFailed(f"Filter query provided but {model} do not have a filterset.")
215
+
216
+ if delete_all:
217
+ if filterset_cls and filter_query_params:
218
+ queryset = filterset_cls(filter_query_params).qs.restrict(self.user, "delete")
219
+ # We take this approach because filterset.qs has already applied .distinct(),
220
+ # and performing a .delete directly on a queryset with .distinct applied is not allowed.
221
+ queryset = model.objects.filter(pk__in=queryset)
222
+ else:
223
+ queryset = model.objects.restrict(self.user, "delete")
224
+ else:
225
+ queryset = model.objects.restrict(self.user, "delete").filter(pk__in=pk_list)
226
+ if queryset.count() < len(pk_list):
227
+ # We do not check ObjectPermission error for `delete_all` case because user is trying to
228
+ # delete all objs they have permission to which would not raise any issue.
229
+ self.logger.error("You do not have permissions to delete some of the objects provided in `pk_list`.")
230
+ raise RunJobTaskFailed("Caught ObjectPermission error while attempting to delete objects")
231
+
232
+ verbose_name_plural = model._meta.verbose_name_plural
233
+
234
+ # Currently the only purpose of a bulk delete form is to perform a `pre_delete` operation
235
+ if form_cls := get_form_for_model(model, form_prefix="BulkDelete"):
236
+ form = form_cls(model, {"pk": pk_list}, delete_all=delete_all)
237
+ if hasattr(form, "perform_pre_delete"):
238
+ form.perform_pre_delete(queryset)
239
+
240
+ try:
241
+ self.logger.info(f"Deleting {queryset.count()} {verbose_name_plural}...")
242
+ _, deleted_info = bulk_delete_with_bulk_change_logging(queryset)
243
+ deleted_count = deleted_info[model._meta.label]
244
+ except ProtectedError as err:
245
+ self.logger.error(f"Caught ProtectedError while attempting to delete objects: {err}")
246
+ raise RunJobTaskFailed("Caught ProtectedError while attempting to delete objects")
247
+ msg = f"Deleted {deleted_count} {model._meta.verbose_name_plural}"
248
+ self.logger.info(msg)
@@ -72,7 +72,7 @@ class LogsCleanup(Job):
72
72
 
73
73
  def run(self, *, cleanup_types, max_age=None): # pylint: disable=arguments-differ
74
74
  if max_age in (None, ""):
75
- max_age = get_settings_or_config("CHANGELOG_RETENTION")
75
+ max_age = get_settings_or_config("CHANGELOG_RETENTION", fallback=90)
76
76
  if max_age == 0:
77
77
  self.logger.warning(
78
78
  "CHANGELOG_RETENTION setting is set to zero, disabling this Job. "
@@ -98,6 +98,7 @@ class Command(BaseCommand):
98
98
  RearPortTemplateFactory,
99
99
  SoftwareImageFileFactory,
100
100
  SoftwareVersionFactory,
101
+ VirtualDeviceContextFactory,
101
102
  )
102
103
  from nautobot.extras.choices import MetadataTypeDataTypeChoices
103
104
  from nautobot.extras.factory import (
@@ -105,6 +106,7 @@ class Command(BaseCommand):
105
106
  DynamicGroupFactory,
106
107
  ExternalIntegrationFactory,
107
108
  JobLogEntryFactory,
109
+ JobQueueFactory,
108
110
  JobResultFactory,
109
111
  MetadataChoiceFactory,
110
112
  MetadataTypeFactory,
@@ -131,6 +133,14 @@ class Command(BaseCommand):
131
133
  )
132
134
  from nautobot.tenancy.factory import TenantFactory, TenantGroupFactory
133
135
  from nautobot.users.factory import UserFactory
136
+ from nautobot.wireless.factory import (
137
+ ControllerManagedDeviceGroupWithMembersFactory,
138
+ RadioProfileFactory,
139
+ RadioProfilesWithMembersFactory,
140
+ SupportedDataRateFactory,
141
+ WirelessNetworkFactory,
142
+ WirelessNetworksWithMembersFactory,
143
+ )
134
144
  except ImportError as err:
135
145
  raise CommandError('Unable to load data factories. Is the "factory-boy" package installed?') from err
136
146
 
@@ -218,6 +228,7 @@ class Command(BaseCommand):
218
228
  _create_batch(ManufacturerFactory, 2, description="without Platforms or DeviceTypes") # Last 2 hard-coded
219
229
  _create_batch(DeviceRedundancyGroupFactory, 20)
220
230
  _create_batch(DeviceFactory, 20)
231
+ _create_batch(VirtualDeviceContextFactory, 30)
221
232
  _create_batch(ModuleFactory, 20)
222
233
  _create_batch(SoftwareVersionFactory, 5, description="without Devices")
223
234
  _create_batch(SoftwareImageFileFactory, 5, description="without DeviceTypes")
@@ -304,6 +315,15 @@ class Command(BaseCommand):
304
315
  _create_batch(ExternalIntegrationFactory, 20)
305
316
  _create_batch(ControllerFactory, 10, description="with Devices or DeviceRedundancyGroups")
306
317
  _create_batch(ControllerManagedDeviceGroupFactory, 5, description="without any Devices")
318
+ _create_batch(SupportedDataRateFactory, 20)
319
+ _create_batch(RadioProfileFactory, 20)
320
+ _create_batch(WirelessNetworkFactory, 20)
321
+ _create_batch(ControllerManagedDeviceGroupWithMembersFactory, 5, description="with members")
322
+ _create_batch(RadioProfilesWithMembersFactory, 5, description="with members")
323
+ _create_batch(WirelessNetworksWithMembersFactory, 5, description="with members")
324
+ # make sure we have some supported data rates that have null relationships to make filter tests happy
325
+ _create_batch(SupportedDataRateFactory, 10, description="without any associated objects")
326
+ _create_batch(JobQueueFactory, 10)
307
327
  # make sure we have some tenants that have null relationships to make filter tests happy
308
328
  _create_batch(TenantFactory, 10, description="without any associated objects")
309
329
  # TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
@@ -329,6 +349,7 @@ class Command(BaseCommand):
329
349
  )
330
350
  _create_batch(MetadataChoiceFactory, 100)
331
351
  _create_batch(ObjectChangeFactory, 100)
352
+ _create_batch(JobQueueFactory, 30)
332
353
  _create_batch(JobResultFactory, 20)
333
354
  _create_batch(JobLogEntryFactory, 100)
334
355
  _create_batch(ObjectMetadataFactory, 100)
@@ -1,9 +1,12 @@
1
+ from zoneinfo import ZoneInfo
2
+
1
3
  from django.conf import settings
2
4
  from django.contrib.auth.middleware import RemoteUserMiddleware as RemoteUserMiddleware_
3
5
  from django.db import ProgrammingError
4
6
  from django.http import Http404
5
7
  from django.urls import resolve
6
8
  from django.urls.exceptions import Resolver404
9
+ from django.utils import timezone
7
10
  from django.utils.deprecation import MiddlewareMixin
8
11
 
9
12
  from nautobot.core.api.utils import is_api_request, rest_api_server_error
@@ -149,3 +152,16 @@ class ExceptionHandlingMiddleware:
149
152
  return server_error(request, template_name=custom_template)
150
153
 
151
154
  return None
155
+
156
+
157
+ class UserDefinedTimeZoneMiddleware:
158
+ def __init__(self, get_response):
159
+ self.get_response = get_response
160
+
161
+ def __call__(self, request):
162
+ if request.user.is_authenticated:
163
+ if tzname := request.user.get_config("timezone"):
164
+ timezone.activate(ZoneInfo(tzname))
165
+ else:
166
+ timezone.deactivate()
167
+ return self.get_response(request)
@@ -280,6 +280,8 @@ class JSONArrayField(models.JSONField):
280
280
  """
281
281
  An ArrayField implementation backed JSON storage.
282
282
  Replicates ArrayField's base field validation.
283
+
284
+ Supports choices in the base field.
283
285
  """
284
286
 
285
287
  _default_hint = ("list", "[]")
@@ -375,13 +377,15 @@ class JSONArrayField(models.JSONField):
375
377
 
376
378
  def formfield(self, **kwargs):
377
379
  """Return a django.forms.Field instance for this field."""
378
- return super().formfield(
379
- **{
380
- "form_class": fields.JSONArrayFormField,
381
- "base_field": self.base_field.formfield(),
382
- **kwargs,
383
- }
384
- )
380
+ defaults = {
381
+ "form_class": fields.JSONArrayFormField,
382
+ "base_field": self.base_field.formfield(),
383
+ }
384
+ # If the base field has choices, pass them to the form field.
385
+ if self.base_field.choices:
386
+ defaults["choices"] = self.base_field.choices
387
+ defaults.update(**kwargs)
388
+ return super().formfield(**defaults)
385
389
 
386
390
 
387
391
  class LaxURLField(models.URLField):
nautobot/core/settings.py CHANGED
@@ -1,3 +1,4 @@
1
+ import datetime
1
2
  import os
2
3
  import os.path
3
4
  import platform
@@ -10,7 +11,11 @@ import django.forms
10
11
  from django.utils.safestring import mark_safe
11
12
 
12
13
  from nautobot import __version__
13
- from nautobot.core.constants import CONFIG_SETTING_SEPARATOR as _CONFIG_SETTING_SEPARATOR
14
+ from nautobot.core.constants import (
15
+ CONFIG_SETTING_SEPARATOR as _CONFIG_SETTING_SEPARATOR,
16
+ MAX_PAGE_SIZE_DEFAULT as _MAX_PAGE_SIZE_DEFAULT,
17
+ PAGINATE_COUNT_DEFAULT as _PAGINATE_COUNT_DEFAULT,
18
+ )
14
19
  from nautobot.core.settings_funcs import ConstanceConfigItem, is_truthy, parse_redis_connection
15
20
 
16
21
  #
@@ -96,6 +101,9 @@ if "NAUTOBOT_DEPLOYMENT_ID" in os.environ and os.environ["NAUTOBOT_DEPLOYMENT_ID
96
101
  if "NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY" in os.environ and os.environ["NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"] != "":
97
102
  DEVICE_NAME_AS_NATURAL_KEY = is_truthy(os.environ["NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"])
98
103
 
104
+ # Event Brokers
105
+ EVENT_BROKERS = {}
106
+
99
107
  # Exclude potentially sensitive models from wildcard view exemption. These may still be exempted
100
108
  # by specifying the model individually in the EXEMPT_VIEW_PERMISSIONS configuration parameter.
101
109
  EXEMPT_EXCLUDE_MODELS = (
@@ -156,6 +164,16 @@ NAPALM_PASSWORD = os.getenv("NAUTOBOT_NAPALM_PASSWORD", "")
156
164
  NAPALM_TIMEOUT = int(os.getenv("NAUTOBOT_NAPALM_TIMEOUT", "30"))
157
165
  NAPALM_USERNAME = os.getenv("NAUTOBOT_NAPALM_USERNAME", "")
158
166
 
167
+ # Expiration date (YYYY-MM-DD) for an active Nautobot support contract with Network to Code.
168
+ # Displayed in the About page.
169
+ if (
170
+ "NAUTOBOT_NTC_SUPPORT_CONTRACT_EXPIRATION_DATE" in os.environ
171
+ and os.environ["NAUTOBOT_NTC_SUPPORT_CONTRACT_EXPIRATION_DATE"] != ""
172
+ ):
173
+ NTC_SUPPORT_CONTRACT_EXPIRATION_DATE = datetime.date.fromisoformat(
174
+ os.environ["NAUTOBOT_NTC_SUPPORT_CONTRACT_EXPIRATION_DATE"]
175
+ )
176
+
159
177
  # Default number of objects to display per page of the UI and REST API. Default is 50
160
178
  if "NAUTOBOT_PAGINATE_COUNT" in os.environ and os.environ["NAUTOBOT_PAGINATE_COUNT"] != "":
161
179
  PAGINATE_COUNT = int(os.environ["NAUTOBOT_PAGINATE_COUNT"])
@@ -404,6 +422,11 @@ SPECTACULAR_SETTINGS = {
404
422
  # enum naming encountered a non-optimally resolvable collision for fields named "protocol".
405
423
  "InterfaceRedundancyGroupProtocolChoices": "nautobot.dcim.choices.InterfaceRedundancyGroupProtocolChoices",
406
424
  "ServiceProtocolChoices": "nautobot.ipam.choices.ServiceProtocolChoices",
425
+ # These choice enums need to be overridden because they get assigned to the `mode` field and
426
+ # result in this error:
427
+ # enum naming encountered a non-optimally resolvable collision for fields named "mode".
428
+ "InterfaceModeChoices": "nautobot.dcim.choices.InterfaceModeChoices",
429
+ "WirelessNetworkModeChoices": "nautobot.wireless.choices.WirelessNetworkModeChoices",
407
430
  },
408
431
  # Create separate schema components for PATCH requests (fields generally are not `required` on PATCH)
409
432
  "COMPONENT_SPLIT_PATCH": True,
@@ -553,6 +576,7 @@ INSTALLED_APPS = [
553
576
  "nautobot.tenancy",
554
577
  "nautobot.users",
555
578
  "nautobot.virtualization",
579
+ "nautobot.wireless",
556
580
  "drf_spectacular",
557
581
  "drf_spectacular_sidecar",
558
582
  "graphene_django",
@@ -583,6 +607,7 @@ MIDDLEWARE = [
583
607
  "nautobot.core.middleware.RemoteUserMiddleware",
584
608
  "nautobot.core.middleware.ExternalAuthMiddleware",
585
609
  "nautobot.core.middleware.ObjectChangeMiddleware",
610
+ "nautobot.core.middleware.UserDefinedTimeZoneMiddleware",
586
611
  "django_prometheus.middleware.PrometheusAfterMiddleware",
587
612
  ]
588
613
 
@@ -707,6 +732,13 @@ CONSTANCE_ADDITIONAL_FIELDS = {
707
732
  "required": False,
708
733
  },
709
734
  ],
735
+ "optional_date_field": [
736
+ "django.forms.DateField",
737
+ {
738
+ "widget": "nautobot.core.forms.widgets.DatePicker",
739
+ "required": False,
740
+ },
741
+ ],
710
742
  }
711
743
 
712
744
  CONSTANCE_CONFIG = {
@@ -761,13 +793,13 @@ CONSTANCE_CONFIG = {
761
793
  field_type=bool,
762
794
  ),
763
795
  "MAX_PAGE_SIZE": ConstanceConfigItem(
764
- default=1000,
796
+ default=_MAX_PAGE_SIZE_DEFAULT,
765
797
  help_text="Maximum number of objects that a user can list in one UI page or one API call.\n"
766
798
  "If set to 0, a user can retrieve an unlimited number of objects.",
767
799
  field_type=int,
768
800
  ),
769
801
  "PAGINATE_COUNT": ConstanceConfigItem(
770
- default=50,
802
+ default=_PAGINATE_COUNT_DEFAULT,
771
803
  help_text="Default number of objects to display per page when listing objects in the UI and/or REST API.",
772
804
  field_type=int,
773
805
  ),
@@ -792,6 +824,12 @@ CONSTANCE_CONFIG = {
792
824
  # Use custom field type defined above
793
825
  field_type="optional_json_field",
794
826
  ),
827
+ "NTC_SUPPORT_CONTRACT_EXPIRATION_DATE": ConstanceConfigItem(
828
+ default="",
829
+ help_text="Expiration date for an active Nautobot support contract with Network to Code. "
830
+ "This value is displayed in the About page to provide additional support information.",
831
+ field_type="optional_date_field",
832
+ ),
795
833
  "PREFER_IPV4": ConstanceConfigItem(
796
834
  default=False,
797
835
  help_text="Whether to prefer IPv4 primary addresses over IPv6 primary addresses for devices.",
@@ -845,7 +883,7 @@ CONSTANCE_CONFIG_FIELDSETS = {
845
883
  "RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT",
846
884
  ],
847
885
  "Release Checking": ["RELEASE_CHECK_URL", "RELEASE_CHECK_TIMEOUT"],
848
- "User Interface": ["SUPPORT_MESSAGE"],
886
+ "User Interface": ["SUPPORT_MESSAGE", "NTC_SUPPORT_CONTRACT_EXPIRATION_DATE"],
849
887
  "Debugging": ["ALLOW_REQUEST_PROFILING"],
850
888
  }
851
889
 
@@ -1113,3 +1151,29 @@ def silk_request_logging_intercept_logic(request):
1113
1151
 
1114
1152
 
1115
1153
  SILKY_INTERCEPT_FUNC = silk_request_logging_intercept_logic
1154
+
1155
+ #
1156
+ # Kubernetes settings variables
1157
+ #
1158
+
1159
+ # Host of the kubernetes pod created in the kubernetes cluster
1160
+ KUBERNETES_DEFAULT_SERVICE_ADDRESS = os.getenv("NAUTOBOT_KUBERNETES_DEFAULT_SERVICE_ADDRESS", "")
1161
+
1162
+ # A dictionary that stores the kubernetes pod manifest used to create a job pod in the kubernetes cluster
1163
+ KUBERNETES_JOB_MANIFEST = {}
1164
+
1165
+ # Name of the kubernetes pod created in the kubernetes cluster
1166
+ KUBERNETES_JOB_POD_NAME = os.getenv("NAUTOBOT_KUBERNETES_JOB_POD_NAME", "")
1167
+
1168
+ # Namespace of the kubernetes pod created in the kubernetes cluster
1169
+ KUBERNETES_JOB_POD_NAMESPACE = os.getenv("NAUTOBOT_KUBERNETES_JOB_POD_NAMESPACE", "")
1170
+
1171
+ # File path to the SSL CA CERT used for authentication to create the job and job pod
1172
+ KUBERNETES_SSL_CA_CERT_PATH = os.getenv(
1173
+ "NAUTOBOT_KUBERNETES_SSL_CA_CERT_PATH", "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
1174
+ )
1175
+
1176
+ # File path to the Bearer token used for authentication to create the job and job pod
1177
+ KUBERNETES_TOKEN_PATH = os.getenv(
1178
+ "NAUTOBOT_KUBERNETES_TOKEN_PATH", "/var/run/secrets/kubernetes.io/serviceaccount/token"
1179
+ )