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
@@ -1,14 +1,20 @@
1
1
  from collections.abc import Mapping
2
2
  from datetime import datetime, timedelta
3
+ import json
3
4
  import logging
4
5
  from pathlib import Path
6
+ import sys
5
7
 
6
8
  from celery import current_app
9
+ from celery.beat import _evaluate_entry_args, _evaluate_entry_kwargs, reraise, SchedulingError
10
+ from celery.result import AsyncResult
7
11
  from django.conf import settings
8
12
  from django_celery_beat.schedulers import DatabaseScheduler, ModelEntry
9
13
  from kombu.utils.json import loads
10
14
 
11
- from nautobot.extras.models import ScheduledJob, ScheduledJobs
15
+ from nautobot.extras.choices import JobQueueTypeChoices
16
+ from nautobot.extras.models import JobResult, ScheduledJob, ScheduledJobs
17
+ from nautobot.extras.utils import run_kubernetes_job_and_return_job_result
12
18
 
13
19
  logger = logging.getLogger(__name__)
14
20
 
@@ -111,7 +117,46 @@ class NautobotDatabaseScheduler(DatabaseScheduler):
111
117
 
112
118
  Ref: https://github.com/celery/django-celery-beat/issues/558#issuecomment-1162730008
113
119
  """
114
- resp = super().apply_async(entry, producer=producer, advance=advance, **kwargs)
120
+ resp = None
121
+ entry = self.reserve(entry) if advance else entry
122
+ task = self.app.tasks.get(entry.task)
123
+
124
+ try:
125
+ entry_args = _evaluate_entry_args(entry.args)
126
+ entry_kwargs = _evaluate_entry_kwargs(entry.kwargs)
127
+ if task:
128
+ scheduled_job = entry.model
129
+ job_queue = scheduled_job.job_queue
130
+ # Distinguish between Celery and Kubernetes job queues
131
+ if job_queue.queue_type == JobQueueTypeChoices.TYPE_KUBERNETES:
132
+ job_result = JobResult.objects.create(
133
+ name=scheduled_job.job_model.name,
134
+ job_model=scheduled_job.job_model,
135
+ scheduled_job=scheduled_job,
136
+ user=scheduled_job.user,
137
+ task_name=scheduled_job.job_model.class_path,
138
+ celery_kwargs=entry.options,
139
+ )
140
+ job_result = run_kubernetes_job_and_return_job_result(
141
+ job_queue, job_result, json.dumps(entry_kwargs)
142
+ )
143
+ # Return an AsyncResult object to mimic the behavior of Celery tasks after the job is finished by Kubernetes Job Pod.
144
+ resp = AsyncResult(job_result.id)
145
+ else:
146
+ resp = task.apply_async(entry_args, entry_kwargs, producer=producer, **entry.options)
147
+ else:
148
+ resp = self.send_task(entry.task, entry_args, entry_kwargs, producer=producer, **entry.options)
149
+ except Exception as exc: # pylint: disable=broad-except
150
+ reraise(
151
+ SchedulingError,
152
+ SchedulingError(f"Couldn't apply scheduled task {entry.name}: {exc}"),
153
+ sys.exc_info()[2],
154
+ )
155
+ finally:
156
+ self._tasks_since_sync += 1
157
+ if self.should_sync():
158
+ self._do_sync()
159
+
115
160
  if entry.total_run_count != entry.model.total_run_count:
116
161
  entry.total_run_count = entry.model.total_run_count
117
162
  entry.model.save()
nautobot/core/choices.py CHANGED
@@ -182,14 +182,14 @@ class ButtonActionColorChoices(ChoiceSet):
182
182
  Map standard button actions to Bootstrap color classes.
183
183
  """
184
184
 
185
- ADD = "info"
185
+ ADD = "primary"
186
186
  CANCEL = "default"
187
187
  CLONE = "success"
188
188
  CONFIGURE = "default"
189
189
  CONNECT = "success"
190
190
  DEFAULT = "default"
191
191
  DELETE = "danger"
192
- DISCONNECT = "info"
192
+ DISCONNECT = "danger"
193
193
  EDIT = "warning"
194
194
  EXPORT = "success"
195
195
  IMPORT = "primary"
@@ -13,6 +13,7 @@ from django.core.management import CommandError, CommandParser, execute_from_com
13
13
  from django.core.management.utils import get_random_secret_key
14
14
  from jinja2 import BaseLoader, Environment
15
15
 
16
+ from nautobot.core.events import load_event_brokers
16
17
  from nautobot.core.settings_funcs import is_truthy
17
18
  from nautobot.extras.plugins.utils import load_plugins
18
19
 
@@ -44,6 +45,7 @@ def _preprocess_settings(settings, config_path):
44
45
  - Set up 'job_logs' database mirror
45
46
  - Handle our custom `STORAGE_BACKEND` setting.
46
47
  - Load plugins based on settings.PLUGINS (potentially affecting INSTALLED_APPS, MIDDLEWARE, and CONSTANCE_CONFIG)
48
+ - Load event brokers based on settings.EVENT_BROKERS
47
49
  """
48
50
  settings.SETTINGS_PATH = config_path
49
51
 
@@ -124,6 +126,12 @@ def _preprocess_settings(settings, config_path):
124
126
  # passed in.
125
127
  load_plugins(settings)
126
128
 
129
+ #
130
+ # Event Broker
131
+ #
132
+
133
+ load_event_brokers(settings.EVENT_BROKERS)
134
+
127
135
 
128
136
  def load_settings(config_path):
129
137
  """Load nautobot_config.py or its equivalent into memory as a `nautobot_config` pseudo-module."""
@@ -110,6 +110,10 @@ CONFIG_SETTING_SEPARATOR = ","
110
110
 
111
111
  CHARFIELD_MAX_LENGTH = 255
112
112
 
113
+ # Default values for pagination settings.
114
+ MAX_PAGE_SIZE_DEFAULT = 1000
115
+ PAGINATE_COUNT_DEFAULT = 50
116
+
113
117
  # Models excluded from the global search list
114
118
  GLOBAL_SEARCH_EXCLUDE_LIST = [
115
119
  "anotherexamplemodel",
@@ -146,12 +150,15 @@ GLOBAL_SEARCH_EXCLUDE_LIST = [
146
150
  "interfaceredundancygroup",
147
151
  "interfaceredundancygroupassociation",
148
152
  "interfacetemplate",
153
+ "interfacevdcassignment",
149
154
  "inventoryitem",
150
155
  "ipaddresstointerface",
151
156
  "job",
152
157
  "jobbutton",
153
158
  "jobhook",
154
159
  "joblogentry",
160
+ "jobqueue",
161
+ "jobqueueassignment",
155
162
  "jobresult",
156
163
  "locationtype",
157
164
  "manufacturer",
@@ -0,0 +1,116 @@
1
+ """Module providing for the publication of event notifications via mechanisms such as Redis, Kafka, syslog, etc."""
2
+
3
+ import fnmatch
4
+ import json
5
+ import logging
6
+
7
+ from django.utils.module_loading import import_string
8
+
9
+ from nautobot.core.celery import NautobotKombuJSONEncoder
10
+ from nautobot.core.events.exceptions import EventBrokerImproperlyConfigured, EventBrokerNotFound
11
+
12
+ from .base import EventBroker
13
+ from .redis_broker import RedisEventBroker
14
+ from .syslog_broker import SyslogEventBroker
15
+
16
+ _EVENT_BROKERS = []
17
+
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def load_event_brokers(event_broker_configs):
23
+ """Process plugins and log errors if they can't be loaded."""
24
+ for broker_name, broker in event_broker_configs.items():
25
+ options = broker.get("OPTIONS", {})
26
+ topics = broker.get("TOPICS", {})
27
+ if not isinstance(topics, dict):
28
+ raise EventBrokerImproperlyConfigured(
29
+ f"{broker_name} Malformed Event Broker Settings: Expected `TOPICS` to be a 'dict', instead a '{type(topics).__name__}' was provided"
30
+ )
31
+ include_topics = topics.get("INCLUDE")
32
+ if include_topics and not isinstance(include_topics, (list, tuple)):
33
+ raise EventBrokerImproperlyConfigured(
34
+ f"{broker_name} Malformed Event Broker Settings: Expected `INCLUDE` to be a 'list' or 'tuple', instead a '{type(include_topics).__name__}' was provided"
35
+ )
36
+ exclude_topics = topics.get("EXCLUDE", [])
37
+ if exclude_topics and not isinstance(exclude_topics, (list, tuple)):
38
+ raise EventBrokerImproperlyConfigured(
39
+ f"{broker_name} Malformed Event Broker Settings: Expected `EXCLUDE` to be a 'list' or 'tuple', instead a '{type(exclude_topics).__name__}' was provided"
40
+ )
41
+ options.update({"include_topics": include_topics, "exclude_topics": exclude_topics})
42
+
43
+ try:
44
+ event_broker_class = import_string(broker["CLASS"])
45
+ if not issubclass(event_broker_class, EventBroker):
46
+ raise EventBrokerImproperlyConfigured(
47
+ f"{broker_name} Malformed Event Broker Settings: Broker provided is not an EventBroker"
48
+ )
49
+ event_broker = event_broker_class(**options)
50
+ register_event_broker(event_broker)
51
+ except ImportError as err:
52
+ raise EventBrokerNotFound(f"Unable to import Event Broker {broker_name}.") from err
53
+
54
+
55
+ def register_event_broker(event_broker):
56
+ """
57
+ Register an `EventBroker` instance for use by Nautobot.
58
+
59
+ The `publish_event()` API will publish events to each registered broker.
60
+ The expectation/intent here, at least initially, is that a given deployment will instantiate zero or more
61
+ EventBrokers, then call `register_event_broker()` for each one, as part of Nautobot initial startup.
62
+
63
+ Args:
64
+ event_broker (EventBroker): The initialized/configured EventBroker instance to register.
65
+ """
66
+ if event_broker not in _EVENT_BROKERS:
67
+ _EVENT_BROKERS.append(event_broker)
68
+ logger.debug("Registered %s as an event broker", event_broker)
69
+ else:
70
+ logger.warning("Tried to register event broker %s but it was already registered", event_broker)
71
+
72
+
73
+ def deregister_event_broker(event_broker):
74
+ """
75
+ Deregister a previously registered `EventBroker` instance so that it no longer receives published events.
76
+ """
77
+ try:
78
+ _EVENT_BROKERS.remove(event_broker)
79
+ logger.debug("Deregistered event broker %s", event_broker)
80
+ except ValueError:
81
+ logger.warning("Tried to deregister event broker %s but it wasn't previously registered", event_broker)
82
+
83
+
84
+ def is_topic_match(topic, patterns):
85
+ return any(fnmatch.fnmatch(topic, pattern) for pattern in patterns)
86
+
87
+
88
+ def publish_event(*, topic, payload):
89
+ """Publish the given event payload to the given topic via all registered `EventBroker` instances.
90
+
91
+ Args:
92
+ topic (str): Topic identifier.
93
+ Convention is to use snake_case and use periods to delineate related groups of topics,
94
+ similar to Python logger naming. For example, you might use `nautobot.create.dcim.device`,
95
+ `nautobot.update.dcim.device`, `nautobot.delete.dcim.device`, `nautobot.create.dcim.interface`,
96
+ `nautobot.create.ipam.ipaddress`, etc.
97
+ payload (dict): JSON-serializable structured data to publish.
98
+ While not all EventBrokers may actually use JSON as their data format, it makes for a reasonable
99
+ lowest common denominator for serializability.
100
+ """
101
+ for event_broker in _EVENT_BROKERS:
102
+ exclude_topics = event_broker.exclude_topics
103
+ include_topics = event_broker.include_topics
104
+ if is_topic_match(topic, include_topics) and not is_topic_match(topic, exclude_topics):
105
+ serialized_payload = json.dumps(payload, cls=NautobotKombuJSONEncoder)
106
+ event_broker.publish(topic=topic, payload=serialized_payload)
107
+
108
+
109
+ __all__ = (
110
+ "EventBroker",
111
+ "RedisEventBroker",
112
+ "SyslogEventBroker",
113
+ "deregister_event_broker",
114
+ "publish_event",
115
+ "register_event_broker",
116
+ )
@@ -0,0 +1,27 @@
1
+ """Base classes for Nautobot event notification framework."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class EventBroker(ABC):
7
+ """Abstract base class for concrete implementations of event brokers such as syslog, Redis, Kafka, etc."""
8
+
9
+ def __init__(self, *args, include_topics=None, exclude_topics=None, **kwargs) -> None:
10
+ self.include_topics = include_topics or ["*"]
11
+ self.exclude_topics = exclude_topics or []
12
+ super().__init__(*args, **kwargs)
13
+
14
+ @abstractmethod
15
+ def publish(self, *, topic, payload):
16
+ """
17
+ Possibly publish the given data `payload` to the given event `topic`.
18
+
19
+ Args:
20
+ topic (str): Topic identifier.
21
+ Convention is to use snake_case and use periods to delineate related groups of topics,
22
+ similar to Python logger naming. For example, you might receive topics such as `create.dcim.device`,
23
+ `update.dcim.device`, `delete.dcim.device`, `create.dcim.interface`, `create.ipam.ipaddress`, etc.
24
+ payload (dict): JSON-serializable structured data to publish.
25
+ While not all EventBrokers may actually use JSON as their data format, it makes for a reasonable
26
+ lowest common denominator for serializability.
27
+ """
@@ -0,0 +1,10 @@
1
+ class EventBrokerError(Exception):
2
+ """Base exception for all event-broker-related errors."""
3
+
4
+
5
+ class EventBrokerNotFound(EventBrokerError):
6
+ """Raised when a specified event broker module cannot be found."""
7
+
8
+
9
+ class EventBrokerImproperlyConfigured(EventBrokerError):
10
+ """Raised when a event is not properly configured."""
@@ -0,0 +1,48 @@
1
+ """Simple EventBroker for publishing events to Redis.
2
+
3
+ To verify that an instance of this broker is working, you can do the following in `nautobot-server nbshell`:
4
+
5
+ ```python
6
+ import json
7
+
8
+ import redis
9
+
10
+
11
+ connection = redis.StrictRedis(host="redis", port=6379, db=2, password="...", charset="utf-8", decode_responses=True)
12
+
13
+
14
+ def listen():
15
+ sub = connection.pubsub()
16
+ sub.psubscribe("nautobot.*")
17
+ for message in sub.listen():
18
+ if message["type"] == "pmessage":
19
+ print(f"Got a pmessage on channel {message['channel']}")
20
+ print(json.dumps(json.loads(message["data"]), indent=2))
21
+
22
+
23
+ listen()
24
+ ```
25
+
26
+ Then perform any action that triggers an event, such as creating or updating a record through the Nautobot UI.
27
+ """
28
+
29
+ import redis
30
+
31
+ from .base import EventBroker
32
+
33
+
34
+ class RedisEventBroker(EventBroker):
35
+ """EventBroker for publishing events to Redis."""
36
+
37
+ def __init__(self, *args, url, **kwargs):
38
+ """Initialize and configure a RedisEventBroker.
39
+
40
+ Args:
41
+ url (str): The Redis URL to connect to.
42
+ """
43
+ self.url = url
44
+ self.connection = redis.StrictRedis.from_url(self.url)
45
+ super().__init__(*args, **kwargs)
46
+
47
+ def publish(self, *, topic, payload):
48
+ self.connection.publish(topic, payload)
@@ -0,0 +1,19 @@
1
+ """Simple EventBroker that just emits syslogs."""
2
+
3
+ import json
4
+ import logging
5
+
6
+ from .base import EventBroker
7
+
8
+
9
+ class SyslogEventBroker(EventBroker):
10
+ def __init__(self, *args, level=logging.INFO, **kwargs):
11
+ """Initialize a SyslogEventBroker that emits logs at the given level."""
12
+ super().__init__(*args, **kwargs)
13
+ self.level = level
14
+
15
+ def publish(self, *, topic, payload):
16
+ logger = logging.getLogger(f"nautobot.events.{topic}")
17
+ # Add an indent of 4 to payload
18
+ payload = json.dumps(json.loads(payload), indent=4)
19
+ logger.log(self.level, "%s", payload)
@@ -33,9 +33,3 @@ class FilterSetFieldNotFound(Exception):
33
33
  """
34
34
  An exception indicating that a filterset field could not be found.
35
35
  """
36
-
37
-
38
- class ViewConfigException(Exception):
39
- """
40
- An exception indicating that a detail view config is invalid.
41
- """
@@ -76,24 +76,21 @@ from nautobot.core.forms.widgets import (
76
76
  )
77
77
 
78
78
  __all__ = (
79
- "add_blank_choice",
80
- "add_field_to_filter_form_class",
81
- "AddressFieldMixin",
82
79
  "ALPHANUMERIC_EXPANSION_PATTERN",
80
+ "BOOLEAN_CHOICES",
81
+ "BOOLEAN_WITH_BLANK_CHOICES",
82
+ "IP4_EXPANSION_PATTERN",
83
+ "IP6_EXPANSION_PATTERN",
84
+ "NUMERIC_EXPANSION_PATTERN",
83
85
  "APISelect",
84
86
  "APISelectMultiple",
87
+ "AddressFieldMixin",
85
88
  "AutoPositionField",
86
89
  "AutoPositionPatternField",
87
- "BOOLEAN_CHOICES",
88
- "BOOLEAN_WITH_BLANK_CHOICES",
89
90
  "BootstrapMixin",
90
91
  "BulkEditForm",
91
92
  "BulkEditNullBooleanSelect",
92
93
  "BulkRenameForm",
93
- "ColorSelect",
94
- "CommentField",
95
- "ConfirmationForm",
96
- "ContentTypeSelect",
97
94
  "CSVChoiceField",
98
95
  "CSVContentTypeField",
99
96
  "CSVDataField",
@@ -102,34 +99,29 @@ __all__ = (
102
99
  "CSVModelForm",
103
100
  "CSVMultipleChoiceField",
104
101
  "CSVMultipleContentTypeField",
102
+ "ColorSelect",
103
+ "CommentField",
104
+ "ConfirmationForm",
105
+ "ContentTypeSelect",
105
106
  "DatePicker",
106
107
  "DateTimePicker",
107
108
  "DynamicFilterForm",
108
109
  "DynamicModelChoiceField",
109
110
  "DynamicModelMultipleChoiceField",
110
- "expand_alphanumeric_pattern",
111
- "expand_ipaddress_pattern",
112
111
  "ExpandableIPAddressField",
113
112
  "ExpandableNameField",
114
- "form_from_model",
115
113
  "ImportForm",
116
- "IP4_EXPANSION_PATTERN",
117
- "IP6_EXPANSION_PATTERN",
118
114
  "JSONArrayFormField",
119
115
  "JSONField",
120
116
  "LaxURLField",
121
117
  "MACAddressField",
122
118
  "MultiMatchModelMultipleChoiceField",
123
- "MultipleContentTypeField",
124
119
  "MultiValueCharField",
125
120
  "MultiValueCharInput",
121
+ "MultipleContentTypeField",
126
122
  "NullableDateField",
127
- "NUMERIC_EXPANSION_PATTERN",
128
123
  "NumericArrayField",
129
- "parse_alphanumeric_range",
130
- "parse_numeric_range",
131
124
  "PrefixFieldMixin",
132
- "restrict_form_fields",
133
125
  "ReturnURLForm",
134
126
  "SearchForm",
135
127
  "SelectWithDisabled",
@@ -142,4 +134,12 @@ __all__ = (
142
134
  "TableConfigForm",
143
135
  "TagFilterField",
144
136
  "TimePicker",
137
+ "add_blank_choice",
138
+ "add_field_to_filter_form_class",
139
+ "expand_alphanumeric_pattern",
140
+ "expand_ipaddress_pattern",
141
+ "form_from_model",
142
+ "parse_alphanumeric_range",
143
+ "parse_numeric_range",
144
+ "restrict_form_fields",
145
145
  )
@@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType
8
8
  from django.contrib.postgres.forms import SimpleArrayField
9
9
  from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, ValidationError
10
10
  from django.db.models import Q
11
- from django.forms.fields import BoundField, InvalidJSONInput, JSONField as _JSONField
11
+ from django.forms.fields import BoundField, CallableChoiceIterator, InvalidJSONInput, JSONField as _JSONField
12
12
  from django.templatetags.static import static
13
13
  from django.urls import reverse
14
14
  from django.utils.html import format_html
@@ -22,7 +22,6 @@ from nautobot.core.models import validators
22
22
  from nautobot.core.utils import data as data_utils, lookup
23
23
 
24
24
  __all__ = (
25
- "CommentField",
26
25
  "CSVChoiceField",
27
26
  "CSVContentTypeField",
28
27
  "CSVDataField",
@@ -30,16 +29,17 @@ __all__ = (
30
29
  "CSVModelChoiceField",
31
30
  "CSVMultipleChoiceField",
32
31
  "CSVMultipleContentTypeField",
32
+ "CommentField",
33
33
  "DynamicModelChoiceField",
34
34
  "DynamicModelMultipleChoiceField",
35
35
  "ExpandableIPAddressField",
36
36
  "ExpandableNameField",
37
- "JSONField",
38
37
  "JSONArrayFormField",
38
+ "JSONField",
39
39
  "LaxURLField",
40
40
  "MACAddressField",
41
- "MultipleContentTypeField",
42
41
  "MultiMatchModelMultipleChoiceField",
42
+ "MultipleContentTypeField",
43
43
  "NumericArrayField",
44
44
  "SlugField",
45
45
  "TagFilterField",
@@ -504,6 +504,8 @@ class DynamicModelChoiceMixin:
504
504
  ):
505
505
  self.display_field = display_field
506
506
  self.query_params = query_params or {}
507
+ # Default to "exclude_m2m=true" for improved performance, if not otherwise specified
508
+ self.query_params.setdefault("exclude_m2m", "true")
507
509
  self.initial_params = initial_params or {}
508
510
  self.null_option = null_option
509
511
  self.disabled_indicator = disabled_indicator
@@ -536,9 +538,10 @@ class DynamicModelChoiceMixin:
536
538
  # Toggle depth
537
539
  attrs["data-depth"] = self.depth
538
540
 
539
- # Attach any static query parameters
540
- for key, value in self.query_params.items():
541
- widget.add_query_param(key, value)
541
+ # Attach any static query parameters if supported
542
+ if isinstance(widget, widgets.APISelect) or hasattr(widget, "add_query_param"):
543
+ for key, value in self.query_params.items():
544
+ widget.add_query_param(key, value)
542
545
 
543
546
  return attrs
544
547
 
@@ -660,11 +663,29 @@ class JSONArrayFormField(django_forms.JSONField):
660
663
  and each Array element is validated by `base_field` validators.
661
664
  """
662
665
 
663
- def __init__(self, base_field, *, delimiter=",", **kwargs):
666
+ def __init__(self, base_field, *, choices=None, delimiter=",", **kwargs):
667
+ self.has_choices = False
668
+ if choices:
669
+ self.choices = choices
670
+ self.widget = widgets.StaticSelect2Multiple(choices=choices)
671
+ self.has_choices = True
664
672
  self.base_field = base_field
665
673
  self.delimiter = delimiter
666
674
  super().__init__(**kwargs)
667
675
 
676
+ # TODO: change this when we upgrade to Django 5, it uses a getter/setter for choices
677
+ def _get_choices(self):
678
+ return getattr(self, "_choices", None)
679
+
680
+ def _set_choices(self, value):
681
+ if callable(value):
682
+ value = CallableChoiceIterator(value)
683
+ else:
684
+ value = list(value)
685
+ self._choices = self.widget.choices = value
686
+
687
+ choices = property(_get_choices, _set_choices)
688
+
668
689
  def clean(self, value):
669
690
  """
670
691
  Validate `value` and return its "cleaned" value as an appropriate
@@ -677,10 +698,21 @@ class JSONArrayFormField(django_forms.JSONField):
677
698
  """
678
699
  Return a string of this value.
679
700
  """
680
- if isinstance(value, list):
701
+ if self.has_choices:
702
+ if isinstance(value, list):
703
+ return value
704
+ return [value]
705
+ elif isinstance(value, list):
681
706
  return self.delimiter.join(str(self.base_field.prepare_value(v)) for v in value)
682
707
  return value
683
708
 
709
+ def bound_data(self, data, initial):
710
+ if data is None:
711
+ return None
712
+ if isinstance(data, list):
713
+ data = json.dumps(data)
714
+ return super().bound_data(data, initial)
715
+
684
716
  def to_python(self, value):
685
717
  """
686
718
  Convert `value` into JSON, raising django.core.exceptions.ValidationError
@@ -718,9 +750,25 @@ class JSONArrayFormField(django_forms.JSONField):
718
750
  self.base_field.validate(item)
719
751
  except ValidationError as error:
720
752
  errors.append(error)
753
+ if self.has_choices and not self.valid_value(item):
754
+ errors.append(ValidationError(f"{item} is not a valid choice"))
721
755
  if errors:
722
756
  raise ValidationError(errors)
723
757
 
758
+ def valid_value(self, value):
759
+ """Check to see if the provided value is a valid choice."""
760
+ text_value = str(value)
761
+ for k, v in self.choices:
762
+ if isinstance(v, (list, tuple)):
763
+ # This is an optgroup, so look inside the group for options
764
+ for k2, _ in v:
765
+ if value == k2 or text_value == str(k2):
766
+ return True
767
+ else:
768
+ if value == k or text_value == str(k):
769
+ return True
770
+ return False
771
+
724
772
  def run_validators(self, value):
725
773
  """
726
774
  Runs all validators against `value` and raise ValidationError if necessary.
@@ -1,8 +1,11 @@
1
+ import contextlib
1
2
  import json
2
3
  import logging
3
4
  import re
4
5
 
5
6
  from django import forms
7
+ from django.core.exceptions import FieldDoesNotExist
8
+ from django.db.models.fields.related import ManyToManyField
6
9
  from django.forms import formset_factory
7
10
  from django.urls import reverse
8
11
  import yaml
@@ -15,8 +18,8 @@ __all__ = (
15
18
  "BootstrapMixin",
16
19
  "BulkEditForm",
17
20
  "BulkRenameForm",
18
- "ConfirmationForm",
19
21
  "CSVModelForm",
22
+ "ConfirmationForm",
20
23
  "DynamicFilterForm",
21
24
  "ImportForm",
22
25
  "PrefixFieldMixin",
@@ -124,7 +127,35 @@ class BulkEditForm(forms.Form):
124
127
 
125
128
  if edit_all:
126
129
  self.fields["pk"].required = False
127
- self.fields["_all"] = forms.BooleanField(widget=forms.HiddenInput(), required=True, initial=True)
130
+ self.fields["_all"] = forms.BooleanField(widget=forms.HiddenInput(), required=False, initial=True)
131
+
132
+ def _save_m2m_fields(self, obj):
133
+ """Save M2M fields"""
134
+ from nautobot.core.models.fields import TagsField # Avoid circular dependency
135
+
136
+ m2m_field_names = []
137
+ # Handle M2M Save
138
+ for key in self.cleaned_data.keys():
139
+ if key.startswith(("add_", "remove_")):
140
+ field_name = key.lstrip("add_")
141
+ if field_name in m2m_field_names:
142
+ continue
143
+ with contextlib.suppress(FieldDoesNotExist):
144
+ field = obj._meta.get_field(field_name)
145
+ is_m2m_field = isinstance(field, (ManyToManyField, TagsField))
146
+ if is_m2m_field:
147
+ m2m_field_names.append(field_name)
148
+
149
+ for field_name in m2m_field_names:
150
+ m2m_field = getattr(obj, field_name)
151
+ if self.cleaned_data.get(f"add_{field_name}", None):
152
+ m2m_field.add(*self.cleaned_data[f"add_{field_name}"])
153
+ if self.cleaned_data.get(f"remove_{field_name}", None):
154
+ m2m_field.remove(*self.cleaned_data[f"remove_{field_name}"])
155
+
156
+ def post_save(self, obj):
157
+ """Post save action"""
158
+ self._save_m2m_fields(obj)
128
159
 
129
160
 
130
161
  class BulkRenameForm(forms.Form):
@@ -43,7 +43,8 @@ def parse_numeric_range(input_string, base=10):
43
43
  begin, end = dash_range, dash_range
44
44
  begin, end = int(begin.strip(), base=base), int(end.strip(), base=base) + 1
45
45
  values.extend(range(begin, end))
46
- return sorted(list(set(values)))
46
+ # Remove duplicates and sort
47
+ return sorted(set(values))
47
48
 
48
49
 
49
50
  def parse_alphanumeric_range(string):