nautobot 3.0.0a3__py3-none-any.whl → 3.0.0rc1__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.
Files changed (388) hide show
  1. nautobot/apps/choices.py +4 -0
  2. nautobot/apps/ui.py +4 -0
  3. nautobot/apps/utils.py +8 -0
  4. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
  5. nautobot/circuits/views.py +6 -2
  6. nautobot/core/api/serializers.py +1 -1
  7. nautobot/core/api/urls.py +1 -0
  8. nautobot/core/api/views.py +4 -0
  9. nautobot/core/choices.py +1 -1
  10. nautobot/core/cli/bootstrap_v3_to_v5.py +36 -13
  11. nautobot/core/cli/migrate_deprecated_templates.py +36 -9
  12. nautobot/core/filters.py +4 -0
  13. nautobot/core/forms/__init__.py +2 -0
  14. nautobot/core/forms/widgets.py +21 -2
  15. nautobot/core/jobs/__init__.py +56 -0
  16. nautobot/core/management/commands/generate_test_data.py +3 -3
  17. nautobot/core/models/__init__.py +11 -0
  18. nautobot/core/models/utils.py +1 -1
  19. nautobot/core/settings.py +17 -7
  20. nautobot/core/settings.yaml +4 -26
  21. nautobot/core/templates/admin/base.html +1 -2
  22. nautobot/core/templates/admin/change_list.html +9 -12
  23. nautobot/core/templates/base_django.html +1 -2
  24. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  25. nautobot/core/templates/components/tab/content_wrapper.html +4 -4
  26. nautobot/core/templates/echarts/echarts.html +21 -8
  27. nautobot/core/templates/generic/object_bulk_create.html +2 -2
  28. nautobot/core/templates/generic/object_bulk_delete.html +1 -1
  29. nautobot/core/templates/generic/object_bulk_edit.html +1 -1
  30. nautobot/core/templates/generic/object_bulk_import.html +1 -1
  31. nautobot/core/templates/generic/object_delete.html +1 -1
  32. nautobot/core/templates/generic/object_detail.html +1 -1
  33. nautobot/core/templates/generic/object_edit.html +1 -1
  34. nautobot/core/templates/generic/object_retrieve.html +2 -2
  35. nautobot/core/templates/graphene/graphiql.html +0 -1
  36. nautobot/core/templates/inc/footer.html +3 -1
  37. nautobot/core/templates/inc/header.html +10 -0
  38. nautobot/core/templates/inc/media.html +14 -0
  39. nautobot/core/templates/inc/nav_menu.html +1 -8
  40. nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
  41. nautobot/core/templates/nautobot_config.py.j2 +0 -6
  42. nautobot/core/templates/rest_framework/api.html +103 -2
  43. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +33 -0
  44. nautobot/core/templates/utilities/theme_preview.html +3 -0
  45. nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
  46. nautobot/core/templatetags/helpers.py +24 -12
  47. nautobot/core/testing/integration.py +24 -13
  48. nautobot/core/testing/utils.py +18 -4
  49. nautobot/core/testing/views.py +104 -17
  50. nautobot/core/tests/integration/test_filters.py +48 -11
  51. nautobot/core/tests/integration/test_theme.py +22 -21
  52. nautobot/core/tests/nautobot_config.py +3 -0
  53. nautobot/core/tests/runner.py +1 -2
  54. nautobot/core/tests/test_breadcrumbs.py +21 -21
  55. nautobot/core/tests/test_jobs.py +73 -6
  56. nautobot/core/tests/test_renderers.py +59 -0
  57. nautobot/core/tests/test_settings_schema.py +1 -0
  58. nautobot/core/tests/test_templatetags_helpers.py +9 -0
  59. nautobot/core/tests/test_titles.py +0 -16
  60. nautobot/core/tests/test_ui.py +122 -3
  61. nautobot/core/tests/test_utils.py +41 -1
  62. nautobot/core/ui/breadcrumbs.py +68 -17
  63. nautobot/core/ui/bulk_buttons.py +1 -1
  64. nautobot/core/ui/choices.py +49 -65
  65. nautobot/core/ui/echarts.py +15 -20
  66. nautobot/core/ui/object_detail.py +54 -46
  67. nautobot/core/ui/titles.py +3 -6
  68. nautobot/core/urls.py +8 -8
  69. nautobot/core/utils/filtering.py +11 -1
  70. nautobot/core/utils/lookup.py +46 -0
  71. nautobot/core/views/mixins.py +31 -20
  72. nautobot/core/views/renderers.py +2 -3
  73. nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -2
  74. nautobot/dcim/api/serializers.py +3 -0
  75. nautobot/dcim/choices.py +49 -0
  76. nautobot/dcim/constants.py +7 -0
  77. nautobot/dcim/factory.py +1 -1
  78. nautobot/dcim/filters.py +13 -1
  79. nautobot/dcim/forms.py +89 -3
  80. nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
  81. nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
  82. nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
  83. nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
  84. nautobot/dcim/migrations/{0078_remove_device_location_tenant_name_uniqueness.py → 0079_remove_device_location_tenant_name_uniqueness.py} +1 -1
  85. nautobot/dcim/migrations/{0079_device_name_data_migration.py → 0080_device_name_data_migration.py} +1 -1
  86. nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
  87. nautobot/dcim/models/device_component_templates.py +33 -1
  88. nautobot/dcim/models/device_components.py +22 -1
  89. nautobot/dcim/models/devices.py +17 -4
  90. nautobot/dcim/tables/devices.py +15 -0
  91. nautobot/dcim/tables/devicetypes.py +8 -1
  92. nautobot/dcim/tables/racks.py +0 -2
  93. nautobot/dcim/tables/template_code.py +1 -1
  94. nautobot/dcim/templates/dcim/cable_trace.html +0 -2
  95. nautobot/dcim/templates/dcim/consoleport.html +1 -1
  96. nautobot/dcim/templates/dcim/consoleserverport.html +1 -1
  97. nautobot/dcim/templates/dcim/devicebay.html +1 -1
  98. nautobot/dcim/templates/dcim/frontport.html +1 -1
  99. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  100. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
  101. nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
  102. nautobot/dcim/templates/dcim/interface.html +9 -1
  103. nautobot/dcim/templates/dcim/interface_edit.html +2 -0
  104. nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
  105. nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
  106. nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
  107. nautobot/dcim/templates/dcim/module_frontports.html +1 -1
  108. nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
  109. nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
  110. nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
  111. nautobot/dcim/templates/dcim/module_powerports.html +1 -1
  112. nautobot/dcim/templates/dcim/module_rearports.html +1 -1
  113. nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
  114. nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
  115. nautobot/dcim/templates/dcim/powerport.html +1 -1
  116. nautobot/dcim/templates/dcim/rack_elevation_list.html +1 -1
  117. nautobot/dcim/templates/dcim/rack_retrieve.html +0 -11
  118. nautobot/dcim/templates/dcim/rearport.html +1 -1
  119. nautobot/dcim/templates/dcim/trace/cable.html +1 -1
  120. nautobot/dcim/templates/dcim/virtualchassis_update.html +1 -1
  121. nautobot/dcim/tests/integration/test_controller.py +3 -6
  122. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
  123. nautobot/dcim/tests/integration/test_create_device.py +0 -2
  124. nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
  125. nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
  126. nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
  127. nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
  128. nautobot/dcim/tests/test_api.py +186 -6
  129. nautobot/dcim/tests/test_filters.py +43 -1
  130. nautobot/dcim/tests/test_forms.py +110 -8
  131. nautobot/dcim/tests/test_graphql.py +44 -1
  132. nautobot/dcim/tests/test_models.py +265 -0
  133. nautobot/dcim/tests/test_tables.py +160 -0
  134. nautobot/dcim/tests/test_views.py +69 -7
  135. nautobot/dcim/views.py +232 -126
  136. nautobot/extras/api/views.py +51 -44
  137. nautobot/extras/datasources/git.py +3 -1
  138. nautobot/extras/filters.py +19 -2
  139. nautobot/extras/forms/forms.py +9 -2
  140. nautobot/extras/jobs.py +2 -0
  141. nautobot/extras/jobs_ui.py +4 -3
  142. nautobot/extras/management/__init__.py +2 -0
  143. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
  144. nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
  145. nautobot/extras/models/approvals.py +11 -1
  146. nautobot/extras/models/change_logging.py +4 -0
  147. nautobot/extras/models/jobs.py +1 -3
  148. nautobot/extras/models/models.py +10 -2
  149. nautobot/extras/plugins/marketplace_manifest.yml +49 -1
  150. nautobot/extras/plugins/views.py +0 -5
  151. nautobot/extras/querysets.py +8 -0
  152. nautobot/extras/tables.py +12 -0
  153. nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
  154. nautobot/extras/templates/extras/configcontext_update.html +1 -0
  155. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  156. nautobot/extras/templates/extras/objectchange_retrieve.html +0 -2
  157. nautobot/extras/templates/extras/plugin_detail.html +3 -3
  158. nautobot/extras/templates/extras/secret_create.html +1 -1
  159. nautobot/extras/tests/integration/test_computedfields.py +8 -9
  160. nautobot/extras/tests/integration/test_customfields.py +1 -3
  161. nautobot/extras/tests/integration/test_dynamicgroups.py +7 -8
  162. nautobot/extras/tests/integration/test_relationships.py +0 -2
  163. nautobot/extras/tests/test_api.py +63 -0
  164. nautobot/extras/tests/test_changelog.py +24 -2
  165. nautobot/extras/tests/test_filters.py +36 -3
  166. nautobot/extras/tests/test_models.py +38 -2
  167. nautobot/extras/tests/test_utils.py +3 -4
  168. nautobot/extras/tests/test_views.py +22 -83
  169. nautobot/extras/urls.py +0 -14
  170. nautobot/extras/views.py +83 -52
  171. nautobot/ipam/filters.py +26 -0
  172. nautobot/ipam/tables.py +6 -0
  173. nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
  174. nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
  175. nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
  176. nautobot/ipam/tests/test_filters.py +26 -1
  177. nautobot/ipam/tests/test_models.py +1 -1
  178. nautobot/ipam/views.py +9 -7
  179. nautobot/load_balancers/__init__.py +0 -0
  180. nautobot/load_balancers/api/__init__.py +1 -0
  181. nautobot/load_balancers/api/serializers.py +75 -0
  182. nautobot/load_balancers/api/urls.py +23 -0
  183. nautobot/load_balancers/api/views.py +61 -0
  184. nautobot/load_balancers/apps.py +17 -0
  185. nautobot/load_balancers/choices.py +167 -0
  186. nautobot/load_balancers/filters.py +225 -0
  187. nautobot/load_balancers/forms.py +532 -0
  188. nautobot/load_balancers/management/commands/__init__.py +0 -0
  189. nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
  190. nautobot/load_balancers/migrations/0001_initial.py +465 -0
  191. nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
  192. nautobot/load_balancers/migrations/__init__.py +0 -0
  193. nautobot/load_balancers/models.py +423 -0
  194. nautobot/load_balancers/navigation.py +80 -0
  195. nautobot/load_balancers/tables.py +255 -0
  196. nautobot/load_balancers/tests/__init__.py +474 -0
  197. nautobot/load_balancers/tests/test_api.py +353 -0
  198. nautobot/load_balancers/tests/test_filters.py +134 -0
  199. nautobot/load_balancers/tests/test_forms.py +266 -0
  200. nautobot/load_balancers/tests/test_models.py +195 -0
  201. nautobot/load_balancers/tests/test_views.py +229 -0
  202. nautobot/load_balancers/urls.py +17 -0
  203. nautobot/load_balancers/views.py +248 -0
  204. nautobot/project-static/dist/css/github-dark.min.css +10 -0
  205. nautobot/project-static/dist/css/github.min.css +10 -0
  206. nautobot/project-static/dist/css/nautobot.css +1 -11
  207. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  208. nautobot/project-static/dist/js/libraries.js +1 -1
  209. nautobot/project-static/dist/js/libraries.js.map +1 -1
  210. nautobot/project-static/dist/js/nautobot.js +1 -1
  211. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  212. nautobot/project-static/js/forms.js +13 -0
  213. nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
  214. nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
  215. nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
  216. nautobot/ui/package-lock.json +87 -4
  217. nautobot/ui/package.json +2 -1
  218. nautobot/ui/src/js/nautobot.js +0 -1
  219. nautobot/ui/src/js/select2.js +53 -2
  220. nautobot/ui/src/scss/nautobot.scss +51 -2
  221. nautobot/ui/webpack.config.js +13 -0
  222. nautobot/users/templates/users/preferences.html +11 -2
  223. nautobot/virtualization/filters.py +6 -1
  224. nautobot/virtualization/tests/test_filters.py +10 -1
  225. nautobot/virtualization/tests/test_models.py +1 -0
  226. nautobot/virtualization/views.py +4 -1
  227. nautobot/vpn/factory.py +25 -15
  228. nautobot/vpn/filters.py +1 -0
  229. nautobot/vpn/forms.py +1 -0
  230. nautobot/vpn/migrations/0001_initial.py +1 -1
  231. nautobot/vpn/models.py +16 -8
  232. nautobot/vpn/tables.py +5 -2
  233. nautobot/vpn/tests/test_api.py +0 -5
  234. nautobot/vpn/tests/test_forms.py +1 -2
  235. nautobot/vpn/tests/test_models.py +57 -7
  236. nautobot/vpn/tests/test_views.py +22 -3
  237. nautobot/vpn/views.py +78 -20
  238. {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +4 -4
  239. {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +243 -352
  240. nautobot/circuits/templates/circuits/circuit.html +0 -2
  241. nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
  242. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
  243. nautobot/circuits/templates/circuits/circuit_update.html +0 -1
  244. nautobot/circuits/templates/circuits/circuittermination.html +0 -2
  245. nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
  246. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
  247. nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
  248. nautobot/circuits/templates/circuits/circuittype.html +0 -2
  249. nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
  250. nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
  251. nautobot/circuits/templates/circuits/provider.html +0 -2
  252. nautobot/circuits/templates/circuits/provider_edit.html +0 -2
  253. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
  254. nautobot/circuits/templates/circuits/provider_update.html +0 -1
  255. nautobot/circuits/templates/circuits/providernetwork.html +0 -2
  256. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
  257. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
  258. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
  259. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
  260. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
  261. nautobot/core/templates/buttons/import.html +0 -9
  262. nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
  263. nautobot/dcim/templates/dcim/cable.html +0 -2
  264. nautobot/dcim/templates/dcim/cable_edit.html +0 -2
  265. nautobot/dcim/templates/dcim/controller/base.html +0 -2
  266. nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
  267. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
  268. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
  269. nautobot/dcim/templates/dcim/device/base.html +0 -2
  270. nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
  271. nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
  272. nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
  273. nautobot/dcim/templates/dcim/device/frontports.html +0 -2
  274. nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
  275. nautobot/dcim/templates/dcim/device/inventory.html +0 -2
  276. nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
  277. nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
  278. nautobot/dcim/templates/dcim/device/powerports.html +0 -2
  279. nautobot/dcim/templates/dcim/device/rearports.html +0 -2
  280. nautobot/dcim/templates/dcim/device/wireless.html +0 -2
  281. nautobot/dcim/templates/dcim/device_component.html +0 -2
  282. nautobot/dcim/templates/dcim/device_edit.html +0 -2
  283. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
  284. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
  285. nautobot/dcim/templates/dcim/devicetype.html +0 -2
  286. nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
  287. nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
  288. nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
  289. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
  290. nautobot/dcim/templates/dcim/location.html +0 -2
  291. nautobot/dcim/templates/dcim/location_edit.html +0 -2
  292. nautobot/dcim/templates/dcim/location_retrieve.html +0 -2
  293. nautobot/dcim/templates/dcim/locationtype.html +0 -2
  294. nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
  295. nautobot/dcim/templates/dcim/manufacturer.html +0 -2
  296. nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
  297. nautobot/dcim/templates/dcim/platform.html +0 -2
  298. nautobot/dcim/templates/dcim/powerfeed.html +0 -2
  299. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
  300. nautobot/dcim/templates/dcim/powerpanel.html +0 -2
  301. nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
  302. nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
  303. nautobot/dcim/templates/dcim/rack.html +0 -2
  304. nautobot/dcim/templates/dcim/rack_edit.html +0 -2
  305. nautobot/dcim/templates/dcim/rackgroup.html +0 -2
  306. nautobot/dcim/templates/dcim/rackreservation.html +0 -2
  307. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
  308. nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
  309. nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
  310. nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
  311. nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
  312. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
  313. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
  314. nautobot/dcim/ui.py +0 -29
  315. nautobot/extras/templates/extras/computedfield.html +0 -2
  316. nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
  317. nautobot/extras/templates/extras/configcontext.html +0 -2
  318. nautobot/extras/templates/extras/configcontext_edit.html +0 -2
  319. nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
  320. nautobot/extras/templates/extras/configcontextschema.html +0 -2
  321. nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
  322. nautobot/extras/templates/extras/contact_retrieve.html +0 -2
  323. nautobot/extras/templates/extras/customfield.html +0 -2
  324. nautobot/extras/templates/extras/customfield_edit.html +0 -2
  325. nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
  326. nautobot/extras/templates/extras/customlink.html +0 -2
  327. nautobot/extras/templates/extras/dynamicgroup.html +0 -2
  328. nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
  329. nautobot/extras/templates/extras/exporttemplate.html +0 -2
  330. nautobot/extras/templates/extras/gitrepository.html +0 -2
  331. nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
  332. nautobot/extras/templates/extras/graphqlquery.html +0 -2
  333. nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
  334. nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -2
  335. nautobot/extras/templates/extras/job_detail.html +0 -2
  336. nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
  337. nautobot/extras/templates/extras/jobhook.html +0 -2
  338. nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
  339. nautobot/extras/templates/extras/jobresult.html +0 -2
  340. nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
  341. nautobot/extras/templates/extras/note.html +0 -2
  342. nautobot/extras/templates/extras/note_retrieve.html +0 -1
  343. nautobot/extras/templates/extras/object_changelog.html +0 -2
  344. nautobot/extras/templates/extras/object_notes.html +0 -2
  345. nautobot/extras/templates/extras/objectchange.html +0 -2
  346. nautobot/extras/templates/extras/objectchange_list.html +0 -3
  347. nautobot/extras/templates/extras/relationship.html +0 -1
  348. nautobot/extras/templates/extras/secret.html +0 -1
  349. nautobot/extras/templates/extras/secret_edit.html +0 -1
  350. nautobot/extras/templates/extras/secretsgroup.html +0 -2
  351. nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
  352. nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
  353. nautobot/extras/templates/extras/status.html +0 -2
  354. nautobot/extras/templates/extras/tag.html +0 -2
  355. nautobot/extras/templates/extras/tag_edit.html +0 -2
  356. nautobot/extras/templates/extras/tag_retrieve.html +0 -2
  357. nautobot/extras/templates/extras/team_retrieve.html +0 -2
  358. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
  359. nautobot/ipam/templates/ipam/prefix.html +0 -2
  360. nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
  361. nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
  362. nautobot/ipam/templates/ipam/rir.html +0 -2
  363. nautobot/ipam/templates/ipam/routetarget.html +0 -1
  364. nautobot/ipam/templates/ipam/service.html +0 -2
  365. nautobot/ipam/templates/ipam/service_edit.html +0 -2
  366. nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
  367. nautobot/ipam/templates/ipam/vlan.html +0 -2
  368. nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
  369. nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
  370. nautobot/ipam/templates/ipam/vlangroup.html +0 -2
  371. nautobot/ipam/templates/ipam/vrf.html +0 -1
  372. nautobot/tenancy/templates/tenancy/tenant.html +0 -2
  373. nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
  374. nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
  375. nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
  376. nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
  377. nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
  378. nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
  379. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
  380. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
  381. nautobot/vpn/templates/vpn/vpnprofile.html +0 -2
  382. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
  383. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
  384. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
  385. {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
  386. {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
  387. {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
  388. {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from django.conf import settings
2
2
  from django.contrib.contenttypes.models import ContentType
3
+ from django.db import transaction
3
4
  from django.db.models import ProtectedError
4
5
  from django.forms import ValidationError as FormsValidationError
5
6
  from django.http import FileResponse, Http404
@@ -897,55 +898,61 @@ class JobViewSetBase(
897
898
  if schedule_data is None:
898
899
  schedule_data = {"interval": JobExecutionType.TYPE_IMMEDIATELY, "start_time": timezone.now()}
899
900
 
900
- schedule = ScheduledJob.create_schedule(
901
- job_model,
902
- request.user,
903
- name=schedule_data.get("name"),
904
- start_time=schedule_data.get("start_time"),
905
- interval=schedule_data.get("interval"),
906
- crontab=schedule_data.get("crontab", ""),
907
- job_queue=job_queue,
908
- validated_save=False,
909
- **job_class.serialize_data(cleaned_data),
910
- )
911
-
912
- scheduled_job_has_approval_workflow = schedule.has_approval_workflow_definition()
913
- if job_model.has_sensitive_variables:
914
- if (
915
- "schedule" in request.data
916
- and "interval" in request.data["schedule"]
917
- and request.data["schedule"]["interval"] != JobExecutionType.TYPE_IMMEDIATELY
918
- ):
919
- raise ValidationError(
920
- {"schedule": {"interval": ["Unable to schedule job: Job may have sensitive input variables"]}}
921
- )
922
- # check approval_required pointer
923
- if scheduled_job_has_approval_workflow:
924
- raise ValidationError(
925
- "Unable to run or schedule job: "
926
- "This job is flagged as possibly having sensitive variables but also has an applicable approval workflow definition."
927
- "Modify or remove the approval workflow definition or modify the job to set `has_sensitive_variables` to False."
928
- )
929
-
930
- # Approval is not required for dryrun
931
- # TODO: remove this once we have the ability to configure an approval workflow to ignore jobs with specific parameters(including `dryrun`)
932
- dryrun = data.get("dryrun", False) if job_class.supports_dryrun else False
933
-
934
- if (not dryrun and scheduled_job_has_approval_workflow) or schedule_data[
935
- "interval"
936
- ] in JobExecutionType.SCHEDULE_CHOICES:
937
- schedule.validated_save()
938
- serializer = serializers.ScheduledJobSerializer(schedule, context={"request": request})
939
- return Response({"scheduled_job": serializer.data, "job_result": None}, status=status.HTTP_201_CREATED)
940
- else:
941
- job_result = JobResult.enqueue_job(
901
+ with transaction.atomic():
902
+ schedule = ScheduledJob.create_schedule(
942
903
  job_model,
943
904
  request.user,
905
+ name=schedule_data.get("name"),
906
+ start_time=schedule_data.get("start_time"),
907
+ interval=schedule_data.get("interval"),
908
+ crontab=schedule_data.get("crontab", ""),
944
909
  job_queue=job_queue,
945
910
  **job_class.serialize_data(cleaned_data),
946
911
  )
947
- serializer = serializers.JobResultSerializer(job_result, context={"request": request})
948
- return Response({"scheduled_job": None, "job_result": serializer.data}, status=status.HTTP_201_CREATED)
912
+
913
+ scheduled_job_has_approval_workflow = schedule.has_approval_workflow_definition()
914
+ if job_model.has_sensitive_variables:
915
+ if (
916
+ "schedule" in request.data
917
+ and "interval" in request.data["schedule"]
918
+ and request.data["schedule"]["interval"] != JobExecutionType.TYPE_IMMEDIATELY
919
+ ):
920
+ schedule.delete()
921
+ schedule = None
922
+ raise ValidationError(
923
+ {"schedule": {"interval": ["Unable to schedule job: Job may have sensitive input variables"]}}
924
+ )
925
+ # check approval_required pointer
926
+ if scheduled_job_has_approval_workflow:
927
+ schedule.delete()
928
+ schedule = None
929
+ raise ValidationError(
930
+ "Unable to run or schedule job: "
931
+ "This job is flagged as possibly having sensitive variables but also has an applicable approval workflow definition."
932
+ "Modify or remove the approval workflow definition or modify the job to set `has_sensitive_variables` to False."
933
+ )
934
+
935
+ # Approval is not required for dryrun
936
+ # TODO: remove this once we have the ability to configure an approval workflow to ignore jobs with specific parameters(including `dryrun`)
937
+ dryrun = data.get("dryrun", False) if job_class.supports_dryrun else False
938
+
939
+ if (not dryrun and scheduled_job_has_approval_workflow) or schedule_data[
940
+ "interval"
941
+ ] in JobExecutionType.SCHEDULE_CHOICES:
942
+ serializer = serializers.ScheduledJobSerializer(schedule, context={"request": request})
943
+ return Response({"scheduled_job": serializer.data, "job_result": None}, status=status.HTTP_201_CREATED)
944
+
945
+ schedule.delete()
946
+ schedule = None
947
+
948
+ job_result = JobResult.enqueue_job(
949
+ job_model,
950
+ request.user,
951
+ job_queue=job_queue,
952
+ **job_class.serialize_data(cleaned_data),
953
+ )
954
+ serializer = serializers.JobResultSerializer(job_result, context={"request": request})
955
+ return Response({"scheduled_job": None, "job_result": serializer.data}, status=status.HTTP_201_CREATED)
949
956
 
950
957
 
951
958
  class JobViewSet(
@@ -18,7 +18,7 @@ import yaml
18
18
 
19
19
  from nautobot.core.utils.git import GitRepo
20
20
  from nautobot.core.utils.module_loading import check_name_safe_to_import_privately, import_modules_privately
21
- from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
21
+ from nautobot.dcim.models import Device, DeviceFamily, DeviceRedundancyGroup, DeviceType, Location, Platform
22
22
  from nautobot.extras.choices import (
23
23
  LogLevelChoices,
24
24
  SecretsGroupAccessTypeChoices,
@@ -274,6 +274,7 @@ def update_git_config_contexts(repository_record, job_result):
274
274
  for filter_type in (
275
275
  "locations",
276
276
  "device_types",
277
+ "device_families",
277
278
  "roles",
278
279
  "platforms",
279
280
  "cluster_groups",
@@ -408,6 +409,7 @@ def import_config_context(context_data, repository_record, job_result):
408
409
  for key, model_class in [
409
410
  ("locations", Location),
410
411
  ("device_types", DeviceType),
412
+ ("device_families", DeviceFamily),
411
413
  ("roles", Role),
412
414
  ("platforms", Platform),
413
415
  ("cluster_groups", ClusterGroup),
@@ -23,8 +23,9 @@ from nautobot.core.filters import (
23
23
  RelatedMembershipBooleanFilter,
24
24
  SearchFilter,
25
25
  )
26
- from nautobot.dcim.models import DeviceRedundancyGroup, DeviceType, Location, Platform
26
+ from nautobot.dcim.models import DeviceFamily, DeviceRedundancyGroup, DeviceType, Location, Platform
27
27
  from nautobot.extras.choices import (
28
+ ApprovalWorkflowStateChoices,
28
29
  JobQueueTypeChoices,
29
30
  JobResultStatusChoices,
30
31
  MetadataTypeDataTypeChoices,
@@ -386,6 +387,12 @@ class ConfigContextFilterSet(BaseFilterSet):
386
387
  to_field_name="model",
387
388
  label="Device Type (model or ID)",
388
389
  )
390
+ device_family = NaturalKeyOrPKMultipleChoiceFilter(
391
+ field_name="device_families",
392
+ queryset=DeviceFamily.objects.all(),
393
+ to_field_name="name",
394
+ label="Device Family (name or ID)",
395
+ )
389
396
  platform_id = ModelMultipleChoiceFilter(
390
397
  field_name="platforms",
391
398
  queryset=Platform.objects.all(),
@@ -1208,10 +1215,15 @@ class ScheduledJobFilterSet(BaseFilterSet):
1208
1215
  label="Time zone",
1209
1216
  null_value="",
1210
1217
  )
1218
+ approval_state = django_filters.MultipleChoiceFilter(
1219
+ field_name="associated_approval_workflows__current_state",
1220
+ label="Approval state",
1221
+ choices=ApprovalWorkflowStateChoices,
1222
+ )
1211
1223
 
1212
1224
  class Meta:
1213
1225
  model = ScheduledJob
1214
- fields = ["id", "name", "total_run_count", "start_time", "last_run_at", "time_zone"]
1226
+ fields = ["id", "name", "enabled", "total_run_count", "start_time", "last_run_at", "time_zone"]
1215
1227
 
1216
1228
 
1217
1229
  #
@@ -1471,6 +1483,11 @@ class SecretFilterSet(
1471
1483
  "name": "icontains",
1472
1484
  },
1473
1485
  )
1486
+ secrets_groups = NaturalKeyOrPKMultipleChoiceFilter(
1487
+ queryset=SecretsGroup.objects.all(),
1488
+ label="Groups (ID or name)",
1489
+ to_field_name="name",
1490
+ )
1474
1491
  # TODO(Glenn): dynamic choices needed. The issue being that secrets providers are Python
1475
1492
  # classes, not database models.
1476
1493
  # provider = django_filters.MultipleChoiceFilter(choices=..., null_value=None)
@@ -42,7 +42,7 @@ from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
42
42
  from nautobot.core.forms.fields import MultiValueCharField
43
43
  from nautobot.core.forms.forms import ConfirmationForm
44
44
  from nautobot.core.forms.widgets import ClearableFileInput
45
- from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
45
+ from nautobot.dcim.models import Device, DeviceFamily, DeviceRedundancyGroup, DeviceType, Location, Platform
46
46
  from nautobot.extras.choices import (
47
47
  ApprovalWorkflowStateChoices,
48
48
  ButtonClassChoices,
@@ -544,6 +544,7 @@ class ConfigContextForm(BootstrapMixin, NoteModelFormMixin, forms.ModelForm):
544
544
  required=False,
545
545
  )
546
546
  device_types = DynamicModelMultipleChoiceField(queryset=DeviceType.objects.all(), required=False)
547
+ device_families = DynamicModelMultipleChoiceField(queryset=DeviceFamily.objects.all(), required=False)
547
548
  platforms = DynamicModelMultipleChoiceField(queryset=Platform.objects.all(), required=False)
548
549
  cluster_groups = DynamicModelMultipleChoiceField(queryset=ClusterGroup.objects.all(), required=False)
549
550
  clusters = DynamicModelMultipleChoiceField(queryset=Cluster.objects.all(), required=False)
@@ -576,6 +577,7 @@ class ConfigContextForm(BootstrapMixin, NoteModelFormMixin, forms.ModelForm):
576
577
  "locations",
577
578
  "roles",
578
579
  "device_types",
580
+ "device_families",
579
581
  "platforms",
580
582
  "cluster_groups",
581
583
  "clusters",
@@ -609,7 +611,12 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
609
611
  role = DynamicModelMultipleChoiceField(
610
612
  queryset=Role.objects.get_for_models([Device, VirtualMachine]), to_field_name="name", required=False
611
613
  )
612
- type = DynamicModelMultipleChoiceField(queryset=DeviceType.objects.all(), to_field_name="model", required=False)
614
+ device_type = DynamicModelMultipleChoiceField(
615
+ queryset=DeviceType.objects.all(), to_field_name="model", required=False
616
+ )
617
+ device_family = DynamicModelMultipleChoiceField(
618
+ queryset=DeviceFamily.objects.all(), to_field_name="name", required=False
619
+ )
613
620
  platform = DynamicModelMultipleChoiceField(queryset=Platform.objects.all(), to_field_name="name", required=False)
614
621
  cluster_group = DynamicModelMultipleChoiceField(
615
622
  queryset=ClusterGroup.objects.all(), to_field_name="name", required=False
nautobot/extras/jobs.py CHANGED
@@ -468,6 +468,7 @@ class BaseJob:
468
468
  label="Profile job execution",
469
469
  help_text="Profiles the job execution using cProfile and outputs a report to /tmp/",
470
470
  )
471
+ form.fields["_profile"].widget.attrs["class"] = "form-check-input"
471
472
  # If the class already exists there may be overrides, so we have to check this.
472
473
  try:
473
474
  job_model = JobModel.objects.get(module_name=cls.__module__, job_class_name=cls.__name__)
@@ -484,6 +485,7 @@ class BaseJob:
484
485
  label="Ignore singleton lock",
485
486
  help_text="Allow this singleton job to run even when another instance is already running",
486
487
  )
488
+ form.fields["_ignore_singleton_lock"].widget.attrs["class"] = "form-check-input"
487
489
 
488
490
  if job_model is not None:
489
491
  job_queue_queryset = JobQueue.objects.filter(jobs=job_model)
@@ -165,10 +165,11 @@ class JobKeyValueOverrideValueTablePanel(KeyValueTablePanel):
165
165
  else:
166
166
  value_tag = format_html(
167
167
  """
168
- <span class="hover_copy">
168
+ <span>
169
169
  <span id="{unique_id}_value_{key}">{value}</span>
170
- <button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{unique_id}_value_{key}">
171
- <span class="mdi mdi-content-copy"></span>
170
+ <button class="btn btn-secondary nb-btn-inline-hover" data-clipboard-target="#{unique_id}_value_{key}">
171
+ <span aria-hidden="true" class="mdi mdi-content-copy"></span>
172
+ <span class="visually-hidden">Copy</span>
172
173
  </button>
173
174
  </span>
174
175
  """,
@@ -10,6 +10,7 @@ from nautobot.core.choices import ColorChoices
10
10
  from nautobot.dcim import choices as dcim_choices
11
11
  from nautobot.extras import choices as extras_choices
12
12
  from nautobot.ipam import choices as ipam_choices
13
+ from nautobot.load_balancers import choices as load_balancer_choices
13
14
  from nautobot.virtualization import choices as vm_choices
14
15
  from nautobot.vpn import choices as vpn_choices
15
16
 
@@ -35,6 +36,7 @@ STATUS_CHOICESET_MAP = {
35
36
  "ipam.Prefix": ipam_choices.PrefixStatusChoices,
36
37
  "ipam.VLAN": ipam_choices.VLANStatusChoices,
37
38
  "ipam.VRF": ipam_choices.VRFStatusChoices,
39
+ "load_balancers.LoadBalancerPoolMember": load_balancer_choices.LoadBalancerPoolMemberStatusChoices,
38
40
  "virtualization.VirtualMachine": vm_choices.VirtualMachineStatusChoices,
39
41
  "virtualization.VMInterface": vm_choices.VMInterfaceStatusChoices,
40
42
  "vpn.VPNTunnel": vpn_choices.VPNTunnelStatusChoices,
@@ -14,4 +14,7 @@ class Command(BaseCommand):
14
14
  dynamic_groups = DynamicGroup.objects.all()
15
15
 
16
16
  for dynamic_group in dynamic_groups:
17
- dynamic_group.update_cached_members()
17
+ try:
18
+ dynamic_group.update_cached_members()
19
+ except Exception as err:
20
+ self.stderr.write(self.style.ERROR(f"Error while refreshing {dynamic_group}: {err}"))
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.24 on 2025-09-26 19:15
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("dcim", "0074_alter_rack_u_height"),
9
+ ("extras", "0130_jobresult_generate_log_entry_counts"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="configcontext",
15
+ name="device_families",
16
+ field=models.ManyToManyField(blank=True, related_name="+", to="dcim.devicefamily"),
17
+ ),
18
+ ]
@@ -4,7 +4,7 @@ from django.conf import settings
4
4
  from django.contrib.auth.models import Group
5
5
  from django.contrib.contenttypes.fields import GenericForeignKey
6
6
  from django.contrib.contenttypes.models import ContentType
7
- from django.core.exceptions import ValidationError
7
+ from django.core.exceptions import FieldError, ValidationError
8
8
  from django.db import models
9
9
  from django.utils import timezone
10
10
 
@@ -97,6 +97,16 @@ class ApprovalWorkflowDefinition(PrimaryModel):
97
97
  """Stringify instance."""
98
98
  return self.name
99
99
 
100
+ def clean(self):
101
+ super().clean()
102
+ model_class = self.model_content_type.model_class()
103
+ if model_class is None:
104
+ raise ValidationError({"model_content_type": "Couldn't find corresponding model class. Is it installed?"})
105
+ try:
106
+ model_class.objects.filter(**self.model_constraints)
107
+ except (FieldError, AttributeError) as exc:
108
+ raise ValidationError({"model_constraints": f"Invalid query filter: {exc}"})
109
+
100
110
 
101
111
  @extras_features(
102
112
  "custom_links",
@@ -154,6 +154,10 @@ class ObjectChange(BaseModel):
154
154
  def __str__(self):
155
155
  return f"{self.changed_object_type} {self.object_repr} {self.get_action_display().lower()} by {self.user_name}"
156
156
 
157
+ @property
158
+ def page_title(self):
159
+ return f"{self.object_repr} - {self.time.strftime('%Y-%m-%d %H:%M')}"
160
+
157
161
  def save(self, *args, **kwargs):
158
162
  # Record the user's name and the object's representation as static strings
159
163
  if not self.user_name:
@@ -1413,7 +1413,6 @@ class ScheduledJob(ApprovableModelMixin, BaseModel):
1413
1413
  job_queue: Optional[JobQueue] = None,
1414
1414
  task_queue: Optional[str] = None, # deprecated!
1415
1415
  ignore_singleton_lock: bool = False,
1416
- validated_save: bool = True,
1417
1416
  **job_kwargs,
1418
1417
  ):
1419
1418
  """
@@ -1503,8 +1502,7 @@ class ScheduledJob(ApprovableModelMixin, BaseModel):
1503
1502
  crontab=crontab,
1504
1503
  job_queue=job_queue,
1505
1504
  )
1506
- if validated_save:
1507
- scheduled_job.validated_save()
1505
+ scheduled_job.validated_save()
1508
1506
  return scheduled_job
1509
1507
 
1510
1508
  create_schedule.__func__.alters_data = True
@@ -31,7 +31,13 @@ from nautobot.extras.choices import (
31
31
  )
32
32
  from nautobot.extras.constants import HTTP_CONTENT_TYPE_JSON
33
33
  from nautobot.extras.models import ChangeLoggedModel
34
- from nautobot.extras.models.mixins import ContactMixin, DynamicGroupsModelMixin, NotesMixin, SavedViewMixin
34
+ from nautobot.extras.models.mixins import (
35
+ ContactMixin,
36
+ DataComplianceModelMixin,
37
+ DynamicGroupsModelMixin,
38
+ NotesMixin,
39
+ SavedViewMixin,
40
+ )
35
41
  from nautobot.extras.models.relationships import RelationshipModel
36
42
  from nautobot.extras.querysets import ConfigContextQuerySet, NotesQuerySet
37
43
  from nautobot.extras.utils import extras_features, FeatureQuery, image_upload
@@ -127,6 +133,7 @@ class ConfigContext(
127
133
  tenant_groups = models.ManyToManyField(to="tenancy.TenantGroup", related_name="+", blank=True)
128
134
  tenants = models.ManyToManyField(to="tenancy.Tenant", related_name="+", blank=True)
129
135
  tags = models.ManyToManyField(to="extras.Tag", related_name="+", blank=True)
136
+ device_families = models.ManyToManyField("dcim.DeviceFamily", related_name="+", blank=True)
130
137
 
131
138
  # Due to feature flag CONFIG_CONTEXT_DYNAMIC_GROUPS_ENABLED this field will remain empty unless set to True.
132
139
  dynamic_groups = models.ManyToManyField(
@@ -687,6 +694,7 @@ class FileProxy(BaseModel):
687
694
  class GraphQLQuery(
688
695
  ChangeLoggedModel,
689
696
  ContactMixin,
697
+ DataComplianceModelMixin,
690
698
  DynamicGroupsModelMixin,
691
699
  NotesMixin,
692
700
  SavedViewMixin,
@@ -827,7 +835,7 @@ class ImageAttachment(BaseModel):
827
835
 
828
836
 
829
837
  @extras_features("graphql", "webhooks")
830
- class Note(ChangeLoggedModel, BaseModel):
838
+ class Note(ChangeLoggedModel, DataComplianceModelMixin, BaseModel):
831
839
  """
832
840
  Notes allow anyone with proper permissions to add a note to an object.
833
841
  """
@@ -1,5 +1,29 @@
1
1
  ---
2
2
  "apps":
3
+ - "name": "Ansible Automation"
4
+ "use_cases":
5
+ - "Network Automation"
6
+ - "Ansible Playbook Execution"
7
+ "requires": []
8
+ "docs": "https://docs.nautobot.com/projects/ansible-automation/en/stable/"
9
+ "headline": >-
10
+ Execute and monitor automation workflows from Redhat Ansible Automation Platform seamlessly without leaving
11
+ Nautobot.
12
+ "description": >-
13
+ Nautobot Ansible Automation seamlessly integrates automation workflows from Redhat Ansible Automation Platform
14
+ as Nautobot Jobs, allowing its users to run and visualize the output of such workflows directly within Nautobot.
15
+ The Nautobot App connects with Redhat AAP/AWX platforms in order to selectively import Job Templates and Workflows
16
+ so they can be triggered to run from Nautobot. Other features include: historical result synchronization, support
17
+ for input customization using surveys, and deeper integration using Nautobot inventory data.
18
+ "package_name": "nautobot_ansible_automation"
19
+ "author": "Network to Code (NTC)"
20
+ "display":
21
+ "cloud": true
22
+ "oss": true
23
+ "enterprise": true
24
+ "availability": "NTC License"
25
+ "icon": >-
26
+ data:image/png;base64,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
3
27
  - "name": "BGP Models"
4
28
  "use_cases":
5
29
  - "Routing"
@@ -112,7 +136,6 @@
112
136
  "availability": "Open Source"
113
137
  "icon": >-
114
138
  data:image/png;base64,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
115
- # TODO: remove data_validation_engine from marketplace repo so it doesn't get reintroduced here by accident
116
139
  - "name": "Design Builder"
117
140
  "use_cases":
118
141
  - "Network Design"
@@ -430,6 +453,31 @@
430
453
  "availability": "Open Source"
431
454
  "icon": >-
432
455
  data:image/png;base64,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
456
+ - "name": "Version Control"
457
+ "use_cases":
458
+ - "Enterprise Governance"
459
+ - "Branching"
460
+ - "Approvals"
461
+ - "Data Management"
462
+ "requires": []
463
+ "docs": "https://docs.nautobot.com/projects/version-control/en/latest/"
464
+ "headline": >-
465
+ Allows users to have change (workflow) management with approvals when managing data within Nautobot ensuring
466
+ Enterprise can maintain governance of their data.
467
+ "description": >-
468
+ Version Control allows users to stage changes and have them reviewed by others before committing them. It offers
469
+ Git semantics when working with Nautobot using branch, commit, merge, and pull request concepts allowing users
470
+ to stage changes in a branch and have it be later merged after someone approves it in the UI or API. This app
471
+ requires a Dolt database.
472
+ "package_name": "nautobot_version_control"
473
+ "author": "Network to Code (NTC)"
474
+ "display":
475
+ "cloud": true
476
+ "oss": false
477
+ "enterprise": false
478
+ "availability": "NTC License"
479
+ "icon": >-
480
+ data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABoVBMVEUAAACvr6+wsLCxsbEAff8AfP8Ae/+BosVYlti1sq61sa7geAbmeADgeAe8oYPIlFvheAbheAiysrKvr6+wsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCvr6+wsLAAfP8AfP8Aff8Aff8Abf+xsbEAff8Aff8Cfv5Xlti7s6oAfP8AfP8kh++orrQAfP8jh++xsLACfv68tKsAef+wsLCwsLCwsLCvr6+xsbGwsLCvsLCxsLCvsLGrr7LCtqgPgfgAdP8Aff8Aff8AfP/CtagAdf8Aff/BtagAff8Aff+rrrL0eADueADfeAfgeAfgeAfuaADgeAbgeAfgeAffeQnIlFqss7vgeAbhdwTWgymyrajhdwSwsLHfeQnJlFqttLzjdQCwsLCwsLCwsLDfeAbheAawsLCwsLCwsLDheQawsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLBnm9FLkjVQAAAAiXRSTlMAAAAAAAAAAAAAAAAAAAAAAAAAAgkCeMLBhg72xfOVDRL8ls+eFggMCIcCCXjCAgL2xcQSCRLC88W/88LFCQmIwHoDA5oNlf6aDJoM+5YNDAz8DAINmgIJAnjCAgn2xcQSCRLC88W/wsUSCQn+Hd0CAk3X+AKM8EGTyAsza/2Qx+/cA6qLTKlqMjF60isAAAL+SURBVFjD7ZfbWxJBGMZlliV1tSgOu8ECghoJ0kpm2ZEsFEMp07IkpDLtqGVaeOhg2XH9q5uZZQ+4s7tDV174Xu3Dw/vb+Wa+eWe2peVQB0YuYCkXFQAAn5+oACUB+II8ScLJEB0B+PmwaPaLETkaoyJAgNhlLqBLkOMJKgIE8H7zBPqF7h46ggIg/Cr0RqkIlgD+VDJBQ7AG+EGMhmAHoCLYAmgIdgCGhmA/AgCSTmthCai31+nenngUdrUDwM02yGNocKFblvsCTgCWTaU1pVjWuMUEISL4HQGp/oym/jNHAJAM+0LknQHpzEBWsWcHzg6egzafvi94KkB2SClg6PyFYTTyoFo3LSCTViYwffHS8GVeDGumpgGZK4NXjXU3XUI2c63VaKIF6JMIx9I8oGEZU/8BaGgkNwngsjgqLDrRBIB+H/GosCtBeaf6HMrlSEeF5STi3QjfCf8hijidZJm0sUnLiADXI3A3iiOwHcGNkZGbKFnyeVI0EBrJCJAgAEgSGIXJlCTGtBVAKUGCJTAw2UbHCuO32nDA7Q8XqxLUSWTQ+sUShb3ihKcVJPNyLgCa60Tsj48XJ0u3Pe0IIIGmOlHxJ+5Mle6WJqYT5nxUG2lGb6QZQyfW/bE29l5psngfTyJDAHAdsw80zXZwKsClnQxudqq4V4ia/HVA58O5sqa5R0f1/q/7AWifHi+Mmf3qCCrl+apir84/fvJUCRSjHz0R3m8AVBcqWAvPFpd4JdL2+RMkvw4oVzisyvPFpRcoVCWGxk8AlF++eq00YYjCTyihWn5zDLsCueV80slPmkQ4FtwBK29lBWDnVwHvjMu46sVX2PfhtXVktPWrgM7ViqYPHIcBH2sbm/omZmxD9biX0+Xl8AhCW1vJ7c31eowwTV40oT7VcmB7Y01ejtr5La66MAvB5y9f+3a+hb/nbP3kyzbKwt0fcry28/PXimTrJ1/3URYGgr///I3VU8n2gyNA+NqQUB/tYvcJh7syikyS0M8u5vCT8KDrH3bO2ltvf7BdAAAAAElFTkSuQmCC
433
481
  - "name": "Welcome Wizard"
434
482
  "use_cases":
435
483
  - "Onboarding"
@@ -141,11 +141,6 @@ class InstalledAppDetailView(GenericView):
141
141
  items={
142
142
  "*": [
143
143
  ViewNameBreadcrumbItem(view_name="apps:apps_list", label="Installed Apps"),
144
- ViewNameBreadcrumbItem(
145
- view_name="apps:app_detail",
146
- reverse_kwargs=lambda context: {"app": context["app_data"]["package"]},
147
- label=lambda context: context["app_data"]["name"],
148
- ),
149
144
  ]
150
145
  }
151
146
  )
@@ -19,6 +19,9 @@ class ConfigContextQuerySet(RestrictedQuerySet):
19
19
  # `device_type` for Device; `type` for VirtualMachine
20
20
  device_type = getattr(obj, "device_type", None)
21
21
 
22
+ # `device_family` for Device;
23
+ device_family = getattr(device_type, "device_family", None)
24
+
22
25
  device_redundancy_group = getattr(obj, "device_redundancy_group", None)
23
26
 
24
27
  # Get the group of the assigned tenant, if any
@@ -40,6 +43,7 @@ class ConfigContextQuerySet(RestrictedQuerySet):
40
43
  Q(locations__in=locations) | Q(locations=None),
41
44
  Q(roles=role) | Q(roles=None),
42
45
  Q(device_types=device_type) | Q(device_types=None),
46
+ Q(device_families=device_family) | Q(device_families=None),
43
47
  Q(platforms=obj.platform) | Q(platforms=None),
44
48
  Q(device_redundancy_groups=device_redundancy_group) | Q(device_redundancy_groups=None),
45
49
  Q(tenant_groups__in=tenant_groups) | Q(tenant_groups=None),
@@ -136,6 +140,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
136
140
  if self.model._meta.model_name == "device":
137
141
  location_query_string = "location"
138
142
  base_query.add((Q(device_types=OuterRef("device_type")) | Q(device_types=None)), Q.AND)
143
+ base_query.add((Q(device_families=OuterRef("device_type__device_family")) | Q(device_families=None)), Q.AND)
139
144
  base_query.add(
140
145
  (Q(device_redundancy_groups=OuterRef("device_redundancy_group")) | Q(device_redundancy_groups=None)),
141
146
  Q.AND,
@@ -145,6 +150,9 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
145
150
  base_query.add((Q(cluster_groups=OuterRef("clusters__cluster_group")) | Q(cluster_groups=None)), Q.AND)
146
151
  else:
147
152
  location_query_string = "cluster__location"
153
+ base_query.add(Q(device_types=None), Q.AND)
154
+ base_query.add(Q(device_families=None), Q.AND)
155
+ base_query.add(Q(device_redundancy_groups=None), Q.AND)
148
156
  # For virtual machines, handle cluster as ForeignKey relationship
149
157
  base_query.add((Q(clusters=OuterRef("cluster")) | Q(clusters=None)), Q.AND)
150
158
  base_query.add((Q(cluster_groups=OuterRef("cluster__cluster_group")) | Q(cluster_groups=None)), Q.AND)
nautobot/extras/tables.py CHANGED
@@ -1161,6 +1161,7 @@ class JobTable(BaseTable):
1161
1161
 
1162
1162
  class JobHookTable(BaseTable):
1163
1163
  pk = ToggleColumn()
1164
+ enabled = BooleanColumn()
1164
1165
  name = tables.Column(linkify=True)
1165
1166
  content_types = tables.TemplateColumn(WEBHOOK_CONTENT_TYPES)
1166
1167
  job = tables.Column(linkify=True)
@@ -1478,12 +1479,20 @@ class ScheduledJobTable(BaseTable):
1478
1479
  pk = ToggleColumn()
1479
1480
  name = tables.Column(linkify=True)
1480
1481
  job_model = tables.Column(verbose_name="Job", linkify=True)
1482
+ enabled = BooleanColumn()
1481
1483
  interval = tables.Column(verbose_name="Execution Type")
1482
1484
  start_time = tables.DateTimeColumn(verbose_name="First Run", format=settings.SHORT_DATETIME_FORMAT)
1483
1485
  last_run_at = tables.DateTimeColumn(verbose_name="Most Recent Run", format=settings.SHORT_DATETIME_FORMAT)
1484
1486
  crontab = tables.Column()
1485
1487
  total_run_count = tables.Column(verbose_name="Total Run Count")
1486
1488
  actions = ButtonsColumn(ScheduledJob, buttons=("delete",), prepend_template=SCHEDULED_JOB_BUTTONS)
1489
+ approval_state = tables.Column(empty_values=[], orderable=False)
1490
+
1491
+ def render_approval_state(self, record):
1492
+ workflow = record.associated_approval_workflows.first()
1493
+ if workflow is not None:
1494
+ return format_html('<a href="{}">{}</a>', record.get_approval_workflow_url(), workflow.current_state)
1495
+ return HTML_NONE
1487
1496
 
1488
1497
  class Meta(BaseTable.Meta):
1489
1498
  model = ScheduledJob
@@ -1492,6 +1501,7 @@ class ScheduledJobTable(BaseTable):
1492
1501
  "name",
1493
1502
  "total_run_count",
1494
1503
  "job_model",
1504
+ "approval_state",
1495
1505
  "interval",
1496
1506
  "start_time",
1497
1507
  "last_run_at",
@@ -1503,6 +1513,8 @@ class ScheduledJobTable(BaseTable):
1503
1513
  "pk",
1504
1514
  "name",
1505
1515
  "job_model",
1516
+ "enabled",
1517
+ "approval_state",
1506
1518
  "interval",
1507
1519
  "last_run_at",
1508
1520
  "actions",
@@ -38,6 +38,8 @@ Ref: https://github.com/nautobot/nautobot/issues/1289
38
38
 
39
39
  _replace_links_{{ ajax_divname }}(url, div.getElementsByTagName('TH'));
40
40
  _replace_links_{{ ajax_divname }}(url, div.getElementsByClassName('pagination'));
41
+ /* Hook back up the JS for the "per_page" dropdown, if present for this table */
42
+ initializeResultPerPageSelection(div);
41
43
  }
42
44
  }
43
45
  };