nautobot 3.0.0a2__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 (659) hide show
  1. nautobot/apps/choices.py +4 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +13 -1
  5. nautobot/apps/utils.py +8 -0
  6. nautobot/circuits/filters.py +3 -2
  7. nautobot/circuits/navigation.py +3 -2
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  10. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  11. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  12. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  13. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
  14. nautobot/circuits/views.py +6 -2
  15. nautobot/cloud/filters.py +1 -1
  16. nautobot/cloud/navigation.py +3 -2
  17. nautobot/core/api/schema.py +1 -1
  18. nautobot/core/api/serializers.py +6 -1
  19. nautobot/core/api/urls.py +2 -0
  20. nautobot/core/api/views.py +12 -0
  21. nautobot/core/apps/__init__.py +11 -10
  22. nautobot/core/celery/__init__.py +3 -5
  23. nautobot/core/checks.py +46 -0
  24. nautobot/core/choices.py +1 -1
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
  26. nautobot/core/cli/migrate_deprecated_templates.py +227 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/filters.py +4 -0
  30. nautobot/core/forms/__init__.py +2 -0
  31. nautobot/core/forms/forms.py +1 -1
  32. nautobot/core/forms/widgets.py +21 -2
  33. nautobot/core/jobs/__init__.py +62 -3
  34. nautobot/core/jobs/groups.py +31 -1
  35. nautobot/core/management/commands/generate_test_data.py +28 -9
  36. nautobot/core/models/__init__.py +11 -0
  37. nautobot/core/models/generics.py +9 -1
  38. nautobot/core/models/tree_queries.py +10 -5
  39. nautobot/core/models/utils.py +1 -1
  40. nautobot/core/settings.py +35 -19
  41. nautobot/core/settings.yaml +17 -33
  42. nautobot/core/signals.py +12 -1
  43. nautobot/core/tables.py +13 -6
  44. nautobot/core/templates/40x.html +1 -1
  45. nautobot/core/templates/500.html +2 -2
  46. nautobot/core/templates/admin/base.html +1 -2
  47. nautobot/core/templates/admin/change_list.html +9 -12
  48. nautobot/core/templates/admin/config/config.html +12 -12
  49. nautobot/core/templates/admin/index.html +3 -3
  50. nautobot/core/templates/base_django.html +1 -2
  51. nautobot/core/templates/buttons/export.html +1 -1
  52. nautobot/core/templates/components/button/dropdown.html +5 -3
  53. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  54. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  55. nautobot/core/templates/components/panel/panel.html +3 -3
  56. nautobot/core/templates/components/tab/content_wrapper.html +6 -7
  57. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  58. nautobot/core/templates/echarts/echarts.html +22 -9
  59. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  60. nautobot/core/templates/generic/object_bulk_create.html +6 -5
  61. nautobot/core/templates/generic/object_bulk_delete.html +1 -1
  62. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  63. nautobot/core/templates/generic/object_bulk_edit.html +1 -1
  64. nautobot/core/templates/generic/object_bulk_import.html +1 -1
  65. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  66. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  67. nautobot/core/templates/generic/object_create.html +5 -4
  68. nautobot/core/templates/generic/object_delete.html +1 -1
  69. nautobot/core/templates/generic/object_detail.html +1 -1
  70. nautobot/core/templates/generic/object_edit.html +1 -1
  71. nautobot/core/templates/generic/object_import.html +2 -1
  72. nautobot/core/templates/generic/object_list.html +12 -4
  73. nautobot/core/templates/generic/object_notes.html +5 -3
  74. nautobot/core/templates/generic/object_retrieve.html +4 -5
  75. nautobot/core/templates/graphene/graphiql.html +7 -8
  76. nautobot/core/templates/home.html +1 -1
  77. nautobot/core/templates/import_success.html +2 -1
  78. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  79. nautobot/core/templates/inc/created_updated.html +7 -3
  80. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  81. nautobot/core/templates/inc/footer.html +3 -1
  82. nautobot/core/templates/inc/form_static_field.html +6 -0
  83. nautobot/core/templates/inc/header.html +11 -1
  84. nautobot/core/templates/inc/image_attachments.html +2 -1
  85. nautobot/core/templates/inc/media.html +14 -0
  86. nautobot/core/templates/inc/nav_menu.html +3 -9
  87. nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
  88. nautobot/core/templates/inc/search_panel.html +4 -4
  89. nautobot/core/templates/login.html +4 -2
  90. nautobot/core/templates/nautobot_config.py.j2 +6 -11
  91. nautobot/core/templates/redoc_ui.html +7 -0
  92. nautobot/core/templates/rest_framework/api.html +103 -2
  93. nautobot/core/templates/search.html +1 -1
  94. nautobot/core/templates/swagger_ui.html +17 -3
  95. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  96. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  97. nautobot/core/templates/utilities/obj_table.html +10 -2
  98. nautobot/core/templates/utilities/render_field.html +7 -7
  99. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  100. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
  101. nautobot/core/templates/utilities/theme_preview.html +19 -3
  102. nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
  103. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  104. nautobot/core/templatetags/helpers.py +76 -18
  105. nautobot/core/testing/api.py +68 -9
  106. nautobot/core/testing/filters.py +0 -23
  107. nautobot/core/testing/integration.py +41 -17
  108. nautobot/core/testing/mixins.py +2 -0
  109. nautobot/core/testing/utils.py +18 -4
  110. nautobot/core/testing/views.py +104 -13
  111. nautobot/core/tests/integration/test_app_home.py +34 -30
  112. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  113. nautobot/core/tests/integration/test_filters.py +48 -11
  114. nautobot/core/tests/integration/test_theme.py +22 -21
  115. nautobot/core/tests/nautobot_config.py +3 -0
  116. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  117. nautobot/core/tests/runner.py +8 -1
  118. nautobot/core/tests/test_api.py +5 -3
  119. nautobot/core/tests/test_breadcrumbs.py +27 -28
  120. nautobot/core/tests/test_checks.py +28 -0
  121. nautobot/core/tests/test_cli.py +40 -0
  122. nautobot/core/tests/test_config.py +2 -1
  123. nautobot/core/tests/test_forms.py +55 -13
  124. nautobot/core/tests/test_jobs.py +144 -3
  125. nautobot/core/tests/test_nautobot_server.py +2 -0
  126. nautobot/core/tests/test_navigations.py +76 -1
  127. nautobot/core/tests/test_patch_social_django.py +42 -0
  128. nautobot/core/tests/test_renderers.py +59 -0
  129. nautobot/core/tests/test_settings_schema.py +1 -0
  130. nautobot/core/tests/test_tables.py +3 -1
  131. nautobot/core/tests/test_templatetags_helpers.py +62 -13
  132. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  133. nautobot/core/tests/test_titles.py +0 -16
  134. nautobot/core/tests/test_tree_queries.py +14 -1
  135. nautobot/core/tests/test_ui.py +123 -4
  136. nautobot/core/tests/test_utils.py +72 -5
  137. nautobot/core/tests/test_views.py +159 -31
  138. nautobot/core/ui/breadcrumbs.py +70 -29
  139. nautobot/core/ui/bulk_buttons.py +1 -1
  140. nautobot/core/ui/choices.py +143 -27
  141. nautobot/core/ui/constants.py +76 -12
  142. nautobot/core/ui/echarts.py +15 -20
  143. nautobot/core/ui/object_detail.py +143 -55
  144. nautobot/core/ui/titles.py +3 -6
  145. nautobot/core/urls.py +20 -9
  146. nautobot/core/utils/cache.py +2 -1
  147. nautobot/core/utils/filtering.py +28 -18
  148. nautobot/core/utils/lookup.py +49 -8
  149. nautobot/core/utils/module_loading.py +21 -0
  150. nautobot/core/utils/patch_social_django.py +128 -0
  151. nautobot/core/views/__init__.py +38 -1
  152. nautobot/core/views/generic.py +3 -3
  153. nautobot/core/views/mixins.py +45 -22
  154. nautobot/core/views/renderers.py +4 -3
  155. nautobot/core/views/viewsets.py +2 -1
  156. nautobot/data_validation/apps.py +1 -5
  157. nautobot/data_validation/custom_validators.py +4 -4
  158. nautobot/data_validation/filters.py +1 -1
  159. nautobot/data_validation/forms.py +40 -0
  160. nautobot/data_validation/migrations/0001_initial.py +0 -7
  161. nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
  162. nautobot/data_validation/models.py +16 -7
  163. nautobot/data_validation/navigation.py +8 -1
  164. nautobot/data_validation/tables.py +12 -5
  165. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  166. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  167. nautobot/data_validation/tests/__init__.py +2 -2
  168. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  169. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  170. nautobot/data_validation/tests/test_filters.py +8 -6
  171. nautobot/data_validation/tests/test_models.py +15 -0
  172. nautobot/data_validation/tests/test_views.py +190 -32
  173. nautobot/data_validation/urls.py +2 -5
  174. nautobot/data_validation/views.py +73 -40
  175. nautobot/dcim/api/serializers.py +3 -13
  176. nautobot/dcim/apps.py +4 -0
  177. nautobot/dcim/choices.py +65 -0
  178. nautobot/dcim/constants.py +7 -0
  179. nautobot/dcim/custom_validators.py +84 -0
  180. nautobot/dcim/factory.py +1 -1
  181. nautobot/dcim/filter_mixins.py +353 -4
  182. nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
  183. nautobot/dcim/forms.py +90 -4
  184. nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
  185. nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
  186. nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
  187. nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
  188. nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
  189. nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
  190. nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
  191. nautobot/dcim/models/device_component_templates.py +33 -1
  192. nautobot/dcim/models/device_components.py +98 -64
  193. nautobot/dcim/models/devices.py +30 -20
  194. nautobot/dcim/navigation.py +7 -6
  195. nautobot/dcim/tables/devices.py +18 -0
  196. nautobot/dcim/tables/devicetypes.py +8 -1
  197. nautobot/dcim/tables/racks.py +0 -2
  198. nautobot/dcim/tables/template_code.py +15 -15
  199. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  200. nautobot/dcim/templates/dcim/cable_trace.html +0 -4
  201. nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
  202. nautobot/dcim/templates/dcim/consoleport.html +7 -6
  203. nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
  204. nautobot/dcim/templates/dcim/device/config.html +2 -2
  205. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  206. nautobot/dcim/templates/dcim/device/status.html +8 -8
  207. nautobot/dcim/templates/dcim/device.html +1 -1
  208. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  209. nautobot/dcim/templates/dcim/device_create.html +5 -3
  210. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  211. nautobot/dcim/templates/dcim/device_list.html +73 -10
  212. nautobot/dcim/templates/dcim/devicebay.html +1 -1
  213. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  214. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  215. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  216. nautobot/dcim/templates/dcim/frontport.html +10 -9
  217. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  218. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  219. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
  220. nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
  221. nautobot/dcim/templates/dcim/interface.html +35 -7
  222. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  223. nautobot/dcim/templates/dcim/interface_edit.html +2 -0
  224. nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
  225. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  226. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  227. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  228. nautobot/dcim/templates/dcim/module/base.html +49 -9
  229. nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
  230. nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
  231. nautobot/dcim/templates/dcim/module_frontports.html +1 -1
  232. nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
  233. nautobot/dcim/templates/dcim/module_list.html +57 -8
  234. nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
  235. nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
  236. nautobot/dcim/templates/dcim/module_powerports.html +1 -1
  237. nautobot/dcim/templates/dcim/module_rearports.html +1 -1
  238. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  239. nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
  240. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  241. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  242. nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
  243. nautobot/dcim/templates/dcim/powerport.html +6 -5
  244. nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
  245. nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
  246. nautobot/dcim/templates/dcim/rearport.html +8 -7
  247. nautobot/dcim/templates/dcim/trace/cable.html +1 -1
  248. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  249. nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
  250. nautobot/dcim/tests/integration/test_controller.py +4 -6
  251. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
  252. nautobot/dcim/tests/integration/test_create_device.py +0 -2
  253. nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
  254. nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
  255. nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
  256. nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
  257. nautobot/dcim/tests/test_api.py +194 -6
  258. nautobot/dcim/tests/test_custom_validators.py +229 -0
  259. nautobot/dcim/tests/test_filters.py +55 -7
  260. nautobot/dcim/tests/test_forms.py +110 -8
  261. nautobot/dcim/tests/test_graphql.py +44 -1
  262. nautobot/dcim/tests/test_models.py +328 -4
  263. nautobot/dcim/tests/test_tables.py +160 -0
  264. nautobot/dcim/tests/test_views.py +132 -29
  265. nautobot/dcim/urls.py +64 -21
  266. nautobot/dcim/utils.py +3 -3
  267. nautobot/dcim/views.py +777 -397
  268. nautobot/extras/api/views.py +60 -45
  269. nautobot/extras/choices.py +2 -13
  270. nautobot/extras/datasources/git.py +3 -1
  271. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  272. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  273. nautobot/extras/{filters/__init__.py → filters.py} +33 -48
  274. nautobot/extras/forms/forms.py +14 -15
  275. nautobot/extras/forms/mixins.py +0 -41
  276. nautobot/extras/jobs.py +2 -0
  277. nautobot/extras/jobs_ui.py +4 -3
  278. nautobot/extras/management/__init__.py +11 -0
  279. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
  280. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  281. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  282. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  283. nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
  284. nautobot/extras/models/__init__.py +1 -2
  285. nautobot/extras/models/approvals.py +33 -14
  286. nautobot/extras/models/change_logging.py +4 -0
  287. nautobot/extras/models/contacts.py +2 -0
  288. nautobot/extras/models/groups.py +44 -5
  289. nautobot/extras/models/jobs.py +60 -4
  290. nautobot/extras/models/mixins.py +28 -0
  291. nautobot/extras/models/models.py +23 -2
  292. nautobot/extras/models/secrets.py +1 -0
  293. nautobot/extras/models/statuses.py +0 -15
  294. nautobot/extras/navigation.py +13 -9
  295. nautobot/extras/plugins/__init__.py +33 -55
  296. nautobot/extras/plugins/marketplace_manifest.yml +49 -1
  297. nautobot/extras/plugins/tables.py +3 -3
  298. nautobot/extras/plugins/urls.py +2 -21
  299. nautobot/extras/plugins/utils.py +1 -33
  300. nautobot/extras/plugins/views.py +0 -9
  301. nautobot/extras/querysets.py +8 -0
  302. nautobot/extras/signals.py +20 -19
  303. nautobot/extras/tables.py +64 -68
  304. nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
  305. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  306. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  307. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  308. nautobot/extras/templates/extras/configcontext_update.html +1 -0
  309. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  310. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  311. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  312. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  313. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  314. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  315. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  316. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  317. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  318. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  319. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  320. nautobot/extras/templates/extras/job.html +29 -12
  321. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  322. nautobot/extras/templates/extras/job_edit.html +52 -46
  323. nautobot/extras/templates/extras/job_list.html +29 -25
  324. nautobot/extras/templates/extras/marketplace.html +5 -9
  325. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  326. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  327. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
  328. nautobot/extras/templates/extras/plugin_detail.html +29 -24
  329. nautobot/extras/templates/extras/plugins_list.html +16 -26
  330. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  331. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  332. nautobot/extras/templates/extras/secret_create.html +1 -1
  333. nautobot/extras/templatetags/custom_links.py +12 -12
  334. nautobot/extras/templatetags/job_buttons.py +14 -12
  335. nautobot/extras/test_jobs/invalid_import.py +9 -0
  336. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  337. nautobot/extras/test_jobs/missing_import.py +11 -0
  338. nautobot/extras/tests/integration/test_computedfields.py +8 -9
  339. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  340. nautobot/extras/tests/integration/test_customfields.py +9 -10
  341. nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
  342. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  343. nautobot/extras/tests/integration/test_plugins.py +18 -6
  344. nautobot/extras/tests/integration/test_relationships.py +0 -2
  345. nautobot/extras/tests/test_api.py +90 -18
  346. nautobot/extras/tests/test_approvals.py +38 -38
  347. nautobot/extras/tests/test_changelog.py +59 -5
  348. nautobot/extras/tests/test_customfields.py +22 -13
  349. nautobot/extras/tests/test_customfields_filters.py +479 -0
  350. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  351. nautobot/extras/tests/test_filters.py +57 -22
  352. nautobot/extras/tests/test_forms.py +18 -21
  353. nautobot/extras/tests/test_jobs.py +25 -4
  354. nautobot/extras/tests/test_migrations.py +1 -0
  355. nautobot/extras/tests/test_models.py +51 -33
  356. nautobot/extras/tests/test_plugins.py +36 -10
  357. nautobot/extras/tests/test_utils.py +3 -4
  358. nautobot/extras/tests/test_views.py +52 -112
  359. nautobot/extras/urls.py +0 -14
  360. nautobot/extras/views.py +164 -71
  361. nautobot/ipam/factory.py +7 -0
  362. nautobot/ipam/filter_mixins.py +38 -0
  363. nautobot/ipam/filters.py +53 -38
  364. nautobot/ipam/formfields.py +1 -1
  365. nautobot/ipam/forms.py +6 -3
  366. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  367. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  368. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  369. nautobot/ipam/models.py +29 -2
  370. nautobot/ipam/navigation.py +3 -2
  371. nautobot/ipam/signals.py +71 -0
  372. nautobot/ipam/tables.py +19 -6
  373. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  374. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  375. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  376. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  377. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  378. nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
  379. nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
  380. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  381. nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
  382. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  383. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  384. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  385. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  386. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  387. nautobot/ipam/tests/test_api.py +13 -6
  388. nautobot/ipam/tests/test_filters.py +36 -1
  389. nautobot/ipam/tests/test_forms.py +1 -1
  390. nautobot/ipam/tests/test_models.py +44 -2
  391. nautobot/ipam/tests/test_tables.py +1 -2
  392. nautobot/ipam/tests/test_utils.py +1 -1
  393. nautobot/ipam/tests/test_views.py +13 -14
  394. nautobot/ipam/ui.py +0 -17
  395. nautobot/ipam/utils/migrations.py +16 -2
  396. nautobot/ipam/utils/testing.py +9 -3
  397. nautobot/ipam/views.py +53 -11
  398. nautobot/load_balancers/__init__.py +0 -0
  399. nautobot/load_balancers/api/__init__.py +1 -0
  400. nautobot/load_balancers/api/serializers.py +75 -0
  401. nautobot/load_balancers/api/urls.py +23 -0
  402. nautobot/load_balancers/api/views.py +61 -0
  403. nautobot/load_balancers/apps.py +17 -0
  404. nautobot/load_balancers/choices.py +167 -0
  405. nautobot/load_balancers/filters.py +225 -0
  406. nautobot/load_balancers/forms.py +532 -0
  407. nautobot/load_balancers/management/commands/__init__.py +0 -0
  408. nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
  409. nautobot/load_balancers/migrations/0001_initial.py +465 -0
  410. nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
  411. nautobot/load_balancers/migrations/__init__.py +0 -0
  412. nautobot/load_balancers/models.py +423 -0
  413. nautobot/load_balancers/navigation.py +80 -0
  414. nautobot/load_balancers/tables.py +255 -0
  415. nautobot/load_balancers/tests/__init__.py +474 -0
  416. nautobot/load_balancers/tests/test_api.py +353 -0
  417. nautobot/load_balancers/tests/test_filters.py +134 -0
  418. nautobot/load_balancers/tests/test_forms.py +266 -0
  419. nautobot/load_balancers/tests/test_models.py +195 -0
  420. nautobot/load_balancers/tests/test_views.py +229 -0
  421. nautobot/load_balancers/urls.py +17 -0
  422. nautobot/load_balancers/views.py +248 -0
  423. nautobot/project-static/dist/css/github-dark.min.css +10 -0
  424. nautobot/project-static/dist/css/github.min.css +10 -0
  425. nautobot/project-static/dist/css/nautobot.css +1 -11
  426. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  427. nautobot/project-static/dist/js/libraries.js +1 -1
  428. nautobot/project-static/dist/js/libraries.js.map +1 -1
  429. nautobot/project-static/dist/js/nautobot.js +1 -1
  430. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  431. nautobot/project-static/js/cabletrace.js +1 -1
  432. nautobot/project-static/js/forms.js +13 -0
  433. nautobot/project-static/js/interface_filtering.js +20 -16
  434. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  435. nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
  436. nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
  437. nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
  438. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  439. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  440. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  441. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  442. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  443. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  444. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  445. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  446. nautobot/project-static/nautobot-icons/security.svg +3 -0
  447. nautobot/project-static/nautobot-icons/server.svg +1 -1
  448. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  449. nautobot/project-static/nautobot-icons/star.svg +1 -1
  450. nautobot/tenancy/api/serializers.py +1 -0
  451. nautobot/tenancy/api/views.py +2 -1
  452. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  453. nautobot/tenancy/navigation.py +3 -1
  454. nautobot/tenancy/tests/test_filters.py +0 -2
  455. nautobot/tenancy/views.py +2 -1
  456. nautobot/ui/package-lock.json +87 -4
  457. nautobot/ui/package.json +2 -1
  458. nautobot/ui/src/js/collapse.js +3 -3
  459. nautobot/ui/src/js/nautobot.js +16 -1
  460. nautobot/ui/src/js/select2.js +53 -2
  461. nautobot/ui/src/scss/colors.scss +1 -1
  462. nautobot/ui/src/scss/nautobot.scss +112 -30
  463. nautobot/ui/webpack.config.js +13 -0
  464. nautobot/users/templates/users/preferences.html +11 -2
  465. nautobot/users/templates/users/profile.html +45 -12
  466. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  467. nautobot/users/tests/test_api.py +4 -0
  468. nautobot/users/views.py +4 -2
  469. nautobot/virtualization/filters.py +6 -1
  470. nautobot/virtualization/models.py +1 -68
  471. nautobot/virtualization/navigation.py +3 -2
  472. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  473. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  474. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  475. nautobot/virtualization/tests/test_api.py +3 -0
  476. nautobot/virtualization/tests/test_filters.py +10 -1
  477. nautobot/virtualization/tests/test_models.py +45 -4
  478. nautobot/virtualization/views.py +4 -1
  479. nautobot/vpn/__init__.py +0 -0
  480. nautobot/vpn/api/serializers.py +113 -0
  481. nautobot/vpn/api/urls.py +19 -0
  482. nautobot/vpn/api/views.py +70 -0
  483. nautobot/vpn/apps.py +8 -0
  484. nautobot/vpn/choices.py +171 -0
  485. nautobot/vpn/factory.py +219 -0
  486. nautobot/vpn/filters.py +234 -0
  487. nautobot/vpn/forms.py +487 -0
  488. nautobot/vpn/homepage.py +19 -0
  489. nautobot/vpn/migrations/0001_initial.py +541 -0
  490. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  491. nautobot/vpn/migrations/__init__.py +0 -0
  492. nautobot/vpn/models.py +535 -0
  493. nautobot/vpn/navigation.py +98 -0
  494. nautobot/vpn/tables.py +383 -0
  495. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  496. nautobot/vpn/tests/__init__.py +0 -0
  497. nautobot/vpn/tests/test_api.py +336 -0
  498. nautobot/vpn/tests/test_filters.py +139 -0
  499. nautobot/vpn/tests/test_forms.py +293 -0
  500. nautobot/vpn/tests/test_models.py +147 -0
  501. nautobot/vpn/tests/test_views.py +300 -0
  502. nautobot/vpn/urls.py +16 -0
  503. nautobot/vpn/views.py +495 -0
  504. nautobot/wireless/navigation.py +3 -2
  505. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  506. nautobot/wireless/tests/test_api.py +1 -1
  507. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
  508. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
  509. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
  510. nautobot/circuits/templates/circuits/circuit.html +0 -2
  511. nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
  512. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
  513. nautobot/circuits/templates/circuits/circuit_update.html +0 -1
  514. nautobot/circuits/templates/circuits/circuittermination.html +0 -2
  515. nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
  516. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
  517. nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
  518. nautobot/circuits/templates/circuits/circuittype.html +0 -2
  519. nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
  520. nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
  521. nautobot/circuits/templates/circuits/provider.html +0 -2
  522. nautobot/circuits/templates/circuits/provider_edit.html +0 -2
  523. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
  524. nautobot/circuits/templates/circuits/provider_update.html +0 -1
  525. nautobot/circuits/templates/circuits/providernetwork.html +0 -2
  526. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
  527. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
  528. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
  529. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
  530. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
  531. nautobot/core/templates/buttons/import.html +0 -9
  532. nautobot/data_validation/template_content.py +0 -42
  533. nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
  534. nautobot/dcim/filters/mixins.py +0 -354
  535. nautobot/dcim/templates/dcim/controller/base.html +0 -2
  536. nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
  537. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
  538. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
  539. nautobot/dcim/templates/dcim/device/base.html +0 -2
  540. nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
  541. nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
  542. nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
  543. nautobot/dcim/templates/dcim/device/frontports.html +0 -2
  544. nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
  545. nautobot/dcim/templates/dcim/device/inventory.html +0 -2
  546. nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
  547. nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
  548. nautobot/dcim/templates/dcim/device/powerports.html +0 -2
  549. nautobot/dcim/templates/dcim/device/rearports.html +0 -2
  550. nautobot/dcim/templates/dcim/device/wireless.html +0 -2
  551. nautobot/dcim/templates/dcim/device_component.html +0 -2
  552. nautobot/dcim/templates/dcim/device_edit.html +0 -2
  553. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
  554. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
  555. nautobot/dcim/templates/dcim/devicetype.html +0 -2
  556. nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
  557. nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
  558. nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
  559. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
  560. nautobot/dcim/templates/dcim/location.html +0 -2
  561. nautobot/dcim/templates/dcim/location_edit.html +0 -2
  562. nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
  563. nautobot/dcim/templates/dcim/locationtype.html +0 -2
  564. nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
  565. nautobot/dcim/templates/dcim/manufacturer.html +0 -2
  566. nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
  567. nautobot/dcim/templates/dcim/platform.html +0 -2
  568. nautobot/dcim/templates/dcim/powerfeed.html +0 -2
  569. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
  570. nautobot/dcim/templates/dcim/powerpanel.html +0 -2
  571. nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
  572. nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
  573. nautobot/dcim/templates/dcim/rack.html +0 -2
  574. nautobot/dcim/templates/dcim/rack_edit.html +0 -2
  575. nautobot/dcim/templates/dcim/rackgroup.html +0 -2
  576. nautobot/dcim/templates/dcim/rackreservation.html +0 -2
  577. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
  578. nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
  579. nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
  580. nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
  581. nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
  582. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
  583. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
  584. nautobot/dcim/ui.py +0 -29
  585. nautobot/extras/templates/extras/computedfield.html +0 -2
  586. nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
  587. nautobot/extras/templates/extras/configcontext.html +0 -2
  588. nautobot/extras/templates/extras/configcontext_edit.html +0 -2
  589. nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
  590. nautobot/extras/templates/extras/configcontextschema.html +0 -2
  591. nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
  592. nautobot/extras/templates/extras/contact_retrieve.html +0 -2
  593. nautobot/extras/templates/extras/customfield.html +0 -2
  594. nautobot/extras/templates/extras/customfield_edit.html +0 -2
  595. nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
  596. nautobot/extras/templates/extras/customlink.html +0 -2
  597. nautobot/extras/templates/extras/dynamicgroup.html +0 -2
  598. nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
  599. nautobot/extras/templates/extras/exporttemplate.html +0 -2
  600. nautobot/extras/templates/extras/gitrepository.html +0 -2
  601. nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
  602. nautobot/extras/templates/extras/graphqlquery.html +0 -2
  603. nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
  604. nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
  605. nautobot/extras/templates/extras/job_detail.html +0 -2
  606. nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
  607. nautobot/extras/templates/extras/jobhook.html +0 -2
  608. nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
  609. nautobot/extras/templates/extras/jobresult.html +0 -2
  610. nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
  611. nautobot/extras/templates/extras/note.html +0 -2
  612. nautobot/extras/templates/extras/note_retrieve.html +0 -1
  613. nautobot/extras/templates/extras/object_changelog.html +0 -2
  614. nautobot/extras/templates/extras/object_notes.html +0 -2
  615. nautobot/extras/templates/extras/objectchange.html +0 -2
  616. nautobot/extras/templates/extras/objectchange_list.html +0 -3
  617. nautobot/extras/templates/extras/relationship.html +0 -1
  618. nautobot/extras/templates/extras/secret.html +0 -1
  619. nautobot/extras/templates/extras/secret_edit.html +0 -1
  620. nautobot/extras/templates/extras/secretsgroup.html +0 -2
  621. nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
  622. nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
  623. nautobot/extras/templates/extras/status.html +0 -2
  624. nautobot/extras/templates/extras/tag.html +0 -2
  625. nautobot/extras/templates/extras/tag_edit.html +0 -2
  626. nautobot/extras/templates/extras/tag_retrieve.html +0 -2
  627. nautobot/extras/templates/extras/team_retrieve.html +0 -2
  628. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  629. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
  630. nautobot/ipam/templates/ipam/prefix.html +0 -2
  631. nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
  632. nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
  633. nautobot/ipam/templates/ipam/rir.html +0 -2
  634. nautobot/ipam/templates/ipam/routetarget.html +0 -1
  635. nautobot/ipam/templates/ipam/service.html +0 -2
  636. nautobot/ipam/templates/ipam/service_edit.html +0 -2
  637. nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
  638. nautobot/ipam/templates/ipam/vlan.html +0 -2
  639. nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
  640. nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
  641. nautobot/ipam/templates/ipam/vlangroup.html +0 -2
  642. nautobot/ipam/templates/ipam/vrf.html +0 -1
  643. nautobot/tenancy/templates/tenancy/tenant.html +0 -2
  644. nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
  645. nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
  646. nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
  647. nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
  648. nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
  649. nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
  650. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
  651. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
  652. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
  653. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
  654. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
  655. /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
  656. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  657. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
  658. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
  659. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
6
6
  from django.contrib.auth.models import Group
7
7
  from django.contrib.contenttypes.models import ContentType
8
8
  from django.core.files.uploadedfile import SimpleUploadedFile
9
- from django.test import override_settings, RequestFactory
9
+ from django.test import override_settings, RequestFactory, tag
10
10
  from django.utils.timezone import now
11
11
 
12
12
  from nautobot.core.jobs import BulkDeleteObjects
@@ -14,6 +14,7 @@ from nautobot.core.testing import FilterTestCases
14
14
  from nautobot.dcim.filters import DeviceFilterSet
15
15
  from nautobot.dcim.models import (
16
16
  Device,
17
+ DeviceFamily,
17
18
  DeviceType,
18
19
  Interface,
19
20
  Location,
@@ -165,35 +166,35 @@ class ApprovalWorkflowTestMixin:
165
166
  cls.approval_workflow_1_definition = ApprovalWorkflowDefinition.objects.create(
166
167
  name="Test Approval Workflow 1 Definition",
167
168
  model_content_type=cls.scheduledjob_ct,
168
- priority=1,
169
+ weight=1,
169
170
  )
170
171
  cls.approval_workflow_2_definition = ApprovalWorkflowDefinition.objects.create(
171
172
  name="Test Approval Workflow 2 Definition",
172
173
  model_content_type=cls.scheduledjob_ct,
173
174
  model_constraints={"name": "Bulk Delete Objects"},
174
- priority=2,
175
+ weight=2,
175
176
  )
176
177
  cls.approval_workflow_3_definition = ApprovalWorkflowDefinition.objects.create(
177
178
  name="Test Approval Workflow 3 Definition",
178
179
  model_content_type=cls.scheduledjob_ct,
179
180
  model_constraints={"name": "Bulk Delete Objects"},
180
- priority=3,
181
+ weight=3,
181
182
  )
182
183
  cls.approval_workflow_4_definition = ApprovalWorkflowDefinition.objects.create(
183
184
  name="Test Approval Workflow 4 Definition",
184
185
  model_content_type=cls.scheduledjob_ct,
185
186
  model_constraints={"name": "Bulk Delete Objects"},
186
- priority=4,
187
+ weight=4,
187
188
  )
188
189
  cls.approval_workflow_5_definition = ApprovalWorkflowDefinition.objects.create(
189
190
  name="Test Approval Workflow 5 Definition",
190
191
  model_content_type=cls.scheduledjob_ct,
191
192
  model_constraints={"name": "Bulk Delete Objects"},
192
- priority=5,
193
+ weight=5,
193
194
  )
194
195
  cls.approval_workflow_1_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
195
196
  approval_workflow_definition=cls.approval_workflow_1_definition,
196
- weight=100,
197
+ sequence=100,
197
198
  name="Test Approval Workflow 1 Stage 1 Definition",
198
199
  min_approvers=2,
199
200
  denial_message="Stage 1 Denial Message",
@@ -201,7 +202,7 @@ class ApprovalWorkflowTestMixin:
201
202
  )
202
203
  cls.approval_workflow_1_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
203
204
  approval_workflow_definition=cls.approval_workflow_1_definition,
204
- weight=200,
205
+ sequence=200,
205
206
  name="Test Approval Workflow 1 Stage 2 Definition",
206
207
  min_approvers=2,
207
208
  denial_message="Stage 2 Denial Message",
@@ -209,7 +210,7 @@ class ApprovalWorkflowTestMixin:
209
210
  )
210
211
  cls.approval_workflow_1_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
211
212
  approval_workflow_definition=cls.approval_workflow_1_definition,
212
- weight=300,
213
+ sequence=300,
213
214
  name="Test Approval Workflow 1 Stage 3 Definition",
214
215
  min_approvers=3,
215
216
  denial_message="Stage 3 Denial Message",
@@ -217,7 +218,7 @@ class ApprovalWorkflowTestMixin:
217
218
  )
218
219
  cls.approval_workflow_1_stage_4_definition = ApprovalWorkflowStageDefinition.objects.create(
219
220
  approval_workflow_definition=cls.approval_workflow_1_definition,
220
- weight=400,
221
+ sequence=400,
221
222
  name="Test Approval Workflow 1 Stage 4 Definition",
222
223
  min_approvers=5,
223
224
  denial_message="Stage 4 Denial Message",
@@ -225,7 +226,7 @@ class ApprovalWorkflowTestMixin:
225
226
  )
226
227
  cls.approval_workflow_1_stage_5_definition = ApprovalWorkflowStageDefinition.objects.create(
227
228
  approval_workflow_definition=cls.approval_workflow_1_definition,
228
- weight=500,
229
+ sequence=500,
229
230
  name="Test Approval Workflow 1 Stage 5 Definition",
230
231
  min_approvers=2,
231
232
  denial_message="Stage 5 Denial Message",
@@ -233,7 +234,7 @@ class ApprovalWorkflowTestMixin:
233
234
  )
234
235
  cls.approval_workflow_1_stage_6_definition = ApprovalWorkflowStageDefinition.objects.create(
235
236
  approval_workflow_definition=cls.approval_workflow_1_definition,
236
- weight=600,
237
+ sequence=600,
237
238
  name="Test Approval Workflow 1 Stage 6 Definition",
238
239
  min_approvers=2,
239
240
  denial_message="Stage 6 Denial Message",
@@ -241,7 +242,7 @@ class ApprovalWorkflowTestMixin:
241
242
  )
242
243
  cls.approval_workflow_2_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
243
244
  approval_workflow_definition=cls.approval_workflow_2_definition,
244
- weight=100,
245
+ sequence=100,
245
246
  name="Test Approval Workflow 2 Stage 1 Definition",
246
247
  min_approvers=2,
247
248
  denial_message="Stage 1 Denial Message",
@@ -249,7 +250,7 @@ class ApprovalWorkflowTestMixin:
249
250
  )
250
251
  cls.approval_workflow_2_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
251
252
  approval_workflow_definition=cls.approval_workflow_2_definition,
252
- weight=200,
253
+ sequence=200,
253
254
  name="Test Approval Workflow 2 Stage 2 Definition",
254
255
  min_approvers=2,
255
256
  denial_message="Stage 2 Denial Message",
@@ -257,7 +258,7 @@ class ApprovalWorkflowTestMixin:
257
258
  )
258
259
  cls.approval_workflow_2_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
259
260
  approval_workflow_definition=cls.approval_workflow_2_definition,
260
- weight=300,
261
+ sequence=300,
261
262
  name="Test Approval Workflow 2 Stage 3 Definition",
262
263
  min_approvers=2,
263
264
  denial_message="Stage 3 Denial Message",
@@ -265,7 +266,7 @@ class ApprovalWorkflowTestMixin:
265
266
  )
266
267
  cls.approval_workflow_3_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
267
268
  approval_workflow_definition=cls.approval_workflow_3_definition,
268
- weight=100,
269
+ sequence=100,
269
270
  name="Test Approval Workflow 3 Stage 1 Definition",
270
271
  min_approvers=2,
271
272
  denial_message="Stage 1 Denial Message",
@@ -273,7 +274,7 @@ class ApprovalWorkflowTestMixin:
273
274
  )
274
275
  cls.approval_workflow_3_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
275
276
  approval_workflow_definition=cls.approval_workflow_3_definition,
276
- weight=200,
277
+ sequence=200,
277
278
  name="Test Approval Workflow 3 Stage 2 Definition",
278
279
  min_approvers=2,
279
280
  denial_message="Stage 2 Denial Message",
@@ -281,7 +282,7 @@ class ApprovalWorkflowTestMixin:
281
282
  )
282
283
  cls.approval_workflow_3_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
283
284
  approval_workflow_definition=cls.approval_workflow_3_definition,
284
- weight=300,
285
+ sequence=300,
285
286
  name="Test Approval Workflow 3 Stage 3 Definition",
286
287
  min_approvers=2,
287
288
  denial_message="Stage 3 Denial Message",
@@ -442,7 +443,7 @@ class ApprovalWorkflowStageDefinitionFilterTestCase(ApprovalWorkflowTestMixin, F
442
443
  ("created",),
443
444
  ("last_updated",),
444
445
  ("approval_workflow_definition",),
445
- ("weight",),
446
+ ("sequence",),
446
447
  ("name",),
447
448
  ("min_approvers",),
448
449
  ("denial_message",),
@@ -605,6 +606,8 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
605
606
  ("cluster_group", "cluster_groups__id"),
606
607
  ("cluster_group", "cluster_groups__name"),
607
608
  ("cluster_group_id", "cluster_groups__id"),
609
+ ("device_family", "device_families__id"),
610
+ ("device_family", "device_families__name"),
608
611
  ("device_type", "device_types__id"),
609
612
  ("device_type", "device_types__model"),
610
613
  ("device_type_id", "device_types__id"),
@@ -631,10 +634,28 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
631
634
 
632
635
  manufacturer = Manufacturer.objects.first()
633
636
 
637
+ cls.device_families = (
638
+ DeviceFamily.objects.create(name="Device Family A"),
639
+ DeviceFamily.objects.create(name="Device Family B"),
640
+ DeviceFamily.objects.create(name="Device Family C"),
641
+ )
642
+
634
643
  device_types = (
635
- DeviceType.objects.create(model="Device Type 1", manufacturer=manufacturer),
636
- DeviceType.objects.create(model="Device Type 2", manufacturer=manufacturer),
637
- DeviceType.objects.create(model="Device Type 3", manufacturer=manufacturer),
644
+ DeviceType.objects.create(
645
+ model="Device Type 1",
646
+ manufacturer=manufacturer,
647
+ device_family=cls.device_families[0],
648
+ ),
649
+ DeviceType.objects.create(
650
+ model="Device Type 2",
651
+ manufacturer=manufacturer,
652
+ device_family=cls.device_families[1],
653
+ ),
654
+ DeviceType.objects.create(
655
+ model="Device Type 3",
656
+ manufacturer=manufacturer,
657
+ device_family=cls.device_families[2],
658
+ ),
638
659
  )
639
660
  cls.device_types = device_types
640
661
 
@@ -669,6 +690,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
669
690
  )
670
691
  c.locations.set([cls.locations[i]])
671
692
  c.roles.set([device_roles[i]])
693
+ c.device_families.set([cls.device_families[i]])
672
694
  c.device_types.set([device_types[i]])
673
695
  c.platforms.set([platforms[i]])
674
696
  c.cluster_groups.set([cluster_groups[i]])
@@ -1331,6 +1353,7 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
1331
1353
  params = {"dryrun_default": True}
1332
1354
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
1333
1355
 
1356
+ @tag("example_app")
1334
1357
  def test_hidden(self):
1335
1358
  params = {"hidden": True}
1336
1359
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
@@ -1341,6 +1364,7 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
1341
1364
  self.filterset(params, self.queryset).qs, self.queryset.filter(read_only=True)
1342
1365
  )
1343
1366
 
1367
+ @tag("example_app")
1344
1368
  def test_is_job_hook_receiver(self):
1345
1369
  params = {"is_job_hook_receiver": True}
1346
1370
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
@@ -2140,6 +2164,8 @@ class SecretTestCase(FilterTestCases.FilterTestCase):
2140
2164
  ("created",),
2141
2165
  ("last_updated",),
2142
2166
  ("name",),
2167
+ ("secrets_groups", "secrets_groups__id"),
2168
+ ("secrets_groups", "secrets_groups__name"),
2143
2169
  ("tags", "tags__id"),
2144
2170
  ("tags", "tags__name"),
2145
2171
  )
@@ -2168,6 +2194,15 @@ class SecretTestCase(FilterTestCases.FilterTestCase):
2168
2194
  secrets[0].tags.set(Tag.objects.get_for_model(Secret))
2169
2195
  secrets[1].tags.set(Tag.objects.get_for_model(Secret)[:3])
2170
2196
 
2197
+ secrets_groups = (
2198
+ SecretsGroup.objects.create(
2199
+ name="Secrets Group 1",
2200
+ ),
2201
+ SecretsGroup.objects.create(name="Secrets Group 2"),
2202
+ )
2203
+ secrets_groups[0].secrets.set([secrets[0]])
2204
+ secrets_groups[1].secrets.set([secrets[1]])
2205
+
2171
2206
  def test_provider(self):
2172
2207
  params = {"provider": ["environment-variable"]}
2173
2208
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from unittest import skip
2
3
  import warnings
3
4
 
4
5
  from django.contrib.auth import get_user_model
@@ -14,14 +15,9 @@ from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoi
14
15
  from nautobot.extras.forms import (
15
16
  ConfigContextFilterForm,
16
17
  ConfigContextForm,
17
- CustomFieldModelBulkEditFormMixin,
18
18
  CustomFieldModelFormMixin,
19
19
  JobButtonForm,
20
20
  JobHookForm,
21
- RelationshipModelFormMixin,
22
- StatusModelBulkEditFormMixin,
23
- StatusModelFilterFormMixin,
24
- TagsBulkEditFormMixin,
25
21
  WebhookForm,
26
22
  )
27
23
  from nautobot.extras.models import (
@@ -1095,29 +1091,30 @@ class WebhookFormTestCase(TestCase):
1095
1091
  )
1096
1092
 
1097
1093
 
1094
+ @skip(reason="Skipping until we have items that need to be deprecated again.")
1098
1095
  class DeprecatedAliasesTestCase(TestCase):
1099
1096
  """Test that deprecated class names still exist, but report a DeprecationWarning when used."""
1100
1097
 
1101
1098
  def test_deprecated_form_mixin_classes(self):
1102
1099
  # Importing these mixin classes doesn't directly warn, but subclassing them does.
1103
- from nautobot.extras.forms import CustomFieldBulkCreateForm
1104
- from nautobot.extras.forms.mixins import (
1105
- AddRemoveTagsForm,
1106
- CustomFieldBulkEditForm,
1107
- CustomFieldModelForm,
1108
- RelationshipModelForm,
1109
- StatusBulkEditFormMixin,
1110
- StatusFilterFormMixin,
1111
- )
1100
+ # from nautobot.extras.forms import CustomFieldBulkCreateForm
1101
+ # from nautobot.extras.forms.mixins import (
1102
+ # AddRemoveTagsForm,
1103
+ # CustomFieldBulkEditForm,
1104
+ # CustomFieldModelForm,
1105
+ # RelationshipModelForm,
1106
+ # StatusBulkEditFormMixin,
1107
+ # StatusFilterFormMixin,
1108
+ # )
1112
1109
 
1113
1110
  for deprecated_form_class, replacement_form_class in (
1114
- (AddRemoveTagsForm, TagsBulkEditFormMixin),
1115
- (CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
1116
- (CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
1117
- (CustomFieldModelForm, CustomFieldModelFormMixin),
1118
- (RelationshipModelForm, RelationshipModelFormMixin),
1119
- (StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
1120
- (StatusFilterFormMixin, StatusModelFilterFormMixin),
1111
+ # (AddRemoveTagsForm, TagsBulkEditFormMixin),
1112
+ # (CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
1113
+ # (CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
1114
+ # (CustomFieldModelForm, CustomFieldModelFormMixin),
1115
+ # (RelationshipModelForm, RelationshipModelFormMixin),
1116
+ # (StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
1117
+ # (StatusFilterFormMixin, StatusModelFilterFormMixin),
1121
1118
  ):
1122
1119
  with self.subTest(msg=f"Replace {deprecated_form_class.__name__} with {replacement_form_class.__name__}"):
1123
1120
  # Subclassing the deprecated class should raise a DeprecationWarning
@@ -16,7 +16,7 @@ from django.core.exceptions import ValidationError
16
16
  from django.core.files.uploadedfile import SimpleUploadedFile
17
17
  from django.core.management import call_command
18
18
  from django.core.management.base import CommandError
19
- from django.test import override_settings
19
+ from django.test import override_settings, tag
20
20
  from django.test.client import RequestFactory
21
21
  from django.utils import timezone
22
22
 
@@ -286,6 +286,7 @@ register_jobs(BadJob)
286
286
  # Clean up back to normal behavior
287
287
  get_jobs(reload=True)
288
288
 
289
+ @tag("example_app")
289
290
  def test_app_dynamic_jobs(self):
290
291
  """
291
292
  Test that get_job() correctly reloads dynamic jobs when `NautobotAppConfig.provides_dynamic_jobs` is True.
@@ -645,6 +646,26 @@ class JobTransactionTest(TransactionTestCase):
645
646
  self.assertFalse(logs.filter(message="I should NOT be logged to the database").exists())
646
647
  self.assertTrue(logs.filter(message="I should be logged to the database").exists())
647
648
 
649
+ def test_log_counts_by_level(self):
650
+ """
651
+ Test that related JobLogEntry counts are stored for JobResult list summary.
652
+ """
653
+ module = "log_counts_by_level"
654
+ name = "TestLogCountsByLevel"
655
+ job_result = create_job_result_and_run_job(module, name)
656
+
657
+ self.assertGreater(job_result.job_log_entries.count(), 0)
658
+ self.assertIsNotNone(job_result.debug_log_count)
659
+ self.assertEqual(job_result.debug_log_count, 0)
660
+ self.assertIsNotNone(job_result.success_log_count)
661
+ self.assertEqual(job_result.success_log_count, 1)
662
+ self.assertIsNotNone(job_result.info_log_count)
663
+ self.assertEqual(job_result.info_log_count, 3)
664
+ self.assertIsNotNone(job_result.warning_log_count)
665
+ self.assertEqual(job_result.warning_log_count, 2)
666
+ self.assertIsNotNone(job_result.error_log_count)
667
+ self.assertEqual(job_result.error_log_count, 3)
668
+
648
669
  def test_object_vars(self):
649
670
  """
650
671
  Test that Object variable fields behave as expected.
@@ -1261,10 +1282,10 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
1261
1282
  status = models.Status.objects.get_for_model(Location).first()
1262
1283
  loc = Location.objects.create(name="Test Job Hook Location 1", location_type=self.location_type, status=status)
1263
1284
  models.ObjectChange.objects.all().delete()
1264
- tag = models.Tag.objects.create(name="A Test Tag")
1265
- tag.content_types.add(ContentType.objects.get_for_model(Location))
1285
+ tag_instance = models.Tag.objects.create(name="A Test Tag")
1286
+ tag_instance.content_types.add(ContentType.objects.get_for_model(Location))
1266
1287
  with web_request_context(user=self.user):
1267
- loc.tags.add(tag)
1288
+ loc.tags.add(tag_instance)
1268
1289
  job_result = models.JobResult.objects.filter(job_model=self.job_model).first()
1269
1290
  self.assertIsNotNone(job_result)
1270
1291
  expected_log_messages = [
@@ -118,6 +118,7 @@ class CustomFieldDataMigrationTest(NautobotDataMigrationTest):
118
118
  self.assertEqual(cf_1.key, "a123_main_ave")
119
119
  self.assertEqual(cf_2.key, "a_456_main_ave")
120
120
 
121
+ @tag("example_app")
121
122
  @skip("Something bad is happening with the test data, suspecting bad merge")
122
123
  def test_custom_field_data_populated_correctly(self):
123
124
  location_0 = self.location.objects.get(name="Test Location 1")
@@ -4,7 +4,6 @@ import shutil
4
4
  import tempfile
5
5
  from unittest import expectedFailure, mock
6
6
  import uuid
7
- import warnings
8
7
  from zoneinfo import ZoneInfo
9
8
 
10
9
  from django.conf import settings
@@ -15,8 +14,7 @@ from django.core.exceptions import ValidationError
15
14
  from django.core.files.uploadedfile import SimpleUploadedFile
16
15
  from django.db.models import ProtectedError
17
16
  from django.db.utils import IntegrityError
18
- from django.test import override_settings
19
- from django.test.utils import isolate_apps
17
+ from django.test import override_settings, tag
20
18
  from django.utils.timezone import get_default_timezone, now
21
19
  from django_celery_beat.tzcrontab import TzAwareCrontab
22
20
  from git import GitCommandError
@@ -29,6 +27,7 @@ from nautobot.core.testing import TestCase
29
27
  from nautobot.core.testing.models import ModelTestCases
30
28
  from nautobot.dcim.models import (
31
29
  Device,
30
+ DeviceFamily,
32
31
  DeviceType,
33
32
  Location,
34
33
  LocationType,
@@ -90,7 +89,6 @@ from nautobot.extras.models import (
90
89
  Team,
91
90
  Webhook,
92
91
  )
93
- from nautobot.extras.models.statuses import StatusModel
94
92
  from nautobot.extras.registry import registry
95
93
  from nautobot.extras.secrets.exceptions import SecretParametersError, SecretProviderError, SecretValueNotFoundError
96
94
  from nautobot.extras.tests.git_helper import create_and_populate_git_repository
@@ -103,8 +101,6 @@ from nautobot.virtualization.models import (
103
101
  VirtualMachine,
104
102
  )
105
103
 
106
- from example_app.jobs import ExampleJob
107
-
108
104
  User = get_user_model()
109
105
 
110
106
 
@@ -146,12 +142,12 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
146
142
  cls.approval_workflow_definition = ApprovalWorkflowDefinition.objects.create(
147
143
  name="Approval Workflow with Three Stages",
148
144
  model_content_type=cls.scheduledjob_ct,
149
- priority=1,
145
+ weight=1,
150
146
  )
151
147
  # Create three stages of the Approval Workflow Definition
152
148
  cls.approval_workflow_stage_definition_1 = ApprovalWorkflowStageDefinition.objects.create(
153
149
  approval_workflow_definition=cls.approval_workflow_definition,
154
- weight=100,
150
+ sequence=100,
155
151
  name="Approval Workflow Stage Definition 1",
156
152
  min_approvers=1,
157
153
  denial_message="Stage 1 Denial Message",
@@ -159,7 +155,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
159
155
  )
160
156
  cls.approval_workflow_stage_definition_2 = ApprovalWorkflowStageDefinition.objects.create(
161
157
  approval_workflow_definition=cls.approval_workflow_definition,
162
- weight=200,
158
+ sequence=200,
163
159
  name="Approval Workflow Stage Definition 2",
164
160
  min_approvers=2,
165
161
  denial_message="Stage 2 Denial Message",
@@ -167,7 +163,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
167
163
  )
168
164
  cls.approval_workflow_stage_definition_3 = ApprovalWorkflowStageDefinition.objects.create(
169
165
  approval_workflow_definition=cls.approval_workflow_definition,
170
- weight=300,
166
+ sequence=300,
171
167
  name="Approval Workflow Stage Definition 3",
172
168
  min_approvers=2,
173
169
  denial_message="Stage 3 Denial Message",
@@ -228,7 +224,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
228
224
  """
229
225
  Test the active_stage property of the ApprovalWorkflow model.
230
226
  """
231
- # Test that the active stage is the one with the lowest weight when all stages are pending
227
+ # Test that the active stage is the one with the lowest sequence when all stages are pending
232
228
  self.assertEqual(self.approval_workflow.active_stage, self.approval_workflow_stage_1)
233
229
  # Test that the active stage is the second stage when the first stage is approved
234
230
  self.approval_workflow_stage_1_response_1.state = ApprovalWorkflowStateChoices.APPROVED
@@ -323,7 +319,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
323
319
  approval_workflow_definition = ApprovalWorkflowDefinition.objects.create(
324
320
  name="Scheduled Job Approval Workflow",
325
321
  model_content_type=scheduled_job_ct,
326
- priority=2,
322
+ weight=2,
327
323
  )
328
324
  approval_workflow = ApprovalWorkflow(
329
325
  approval_workflow_definition=approval_workflow_definition,
@@ -682,8 +678,11 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
682
678
 
683
679
  @classmethod
684
680
  def setUpTestData(cls):
685
- manufacturer = Manufacturer.objects.first()
686
- cls.devicetype = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1")
681
+ cls.manufacturer = Manufacturer.objects.first()
682
+ cls.devicefamily = DeviceFamily.objects.create(name="Device Family 1")
683
+ cls.devicetype = DeviceType.objects.create(
684
+ manufacturer=cls.manufacturer, model="Device Type 1", device_family=cls.devicefamily
685
+ )
687
686
  cls.devicerole = Role.objects.get_for_model(Device).first()
688
687
  root_location_type = LocationType.objects.create(name="Root Location Type")
689
688
  parent_location_type = LocationType.objects.create(name="Parent Location Type", parent=root_location_type)
@@ -1222,6 +1221,38 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
1222
1221
  self.assertNotIn("cluster_group_2", context.keys())
1223
1222
  self.assertNotIn("cluster_group_12", context.keys())
1224
1223
 
1224
+ def test_device_family_context(self):
1225
+ """
1226
+ A config context assigned to the device's DeviceFamily is included in get_config_context().
1227
+ """
1228
+
1229
+ # Create a Family-level context
1230
+ cc_family = ConfigContext.objects.create(
1231
+ name="Device Family 1",
1232
+ weight=100,
1233
+ data={
1234
+ "device_family": "Device Family 1",
1235
+ },
1236
+ )
1237
+ cc_family.device_families.add(self.devicefamily)
1238
+ ctx1 = self.device.get_config_context()
1239
+
1240
+ # Create a second device for a negative test and verify that it does NOT receive the family context
1241
+ device_type2 = DeviceType.objects.create(manufacturer=self.manufacturer, model="Device Type2")
1242
+ device2 = Device.objects.create(
1243
+ name="Device 2",
1244
+ location=self.location,
1245
+ tenant=self.tenant,
1246
+ platform=self.platform,
1247
+ role=self.devicerole,
1248
+ status=self.device_status,
1249
+ device_type=device_type2,
1250
+ )
1251
+ ctx2 = device2.get_config_context()
1252
+
1253
+ self.assertEqual(ctx1.get("device_family"), "Device Family 1")
1254
+ self.assertEqual(ctx2.get("device_family"), None)
1255
+
1225
1256
 
1226
1257
  class ConfigContextSchemaTestCase(ModelTestCases.BaseModelTestCase):
1227
1258
  """
@@ -1961,6 +1992,7 @@ class GitRepositoryTest(ModelTestCases.BaseModelTestCase):
1961
1992
  shutil.rmtree(self.tempdir.name, ignore_errors=True)
1962
1993
 
1963
1994
 
1995
+ @tag("example_app")
1964
1996
  class JobModelTest(ModelTestCases.BaseModelTestCase):
1965
1997
  """
1966
1998
  Tests for the `Job` model class.
@@ -1976,6 +2008,8 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1976
2008
  cls.app_job = JobModel.objects.get(job_class_name="ExampleJob")
1977
2009
 
1978
2010
  def test_job_class(self):
2011
+ from example_app.jobs import ExampleJob
2012
+
1979
2013
  self.assertIsNotNone(self.local_job.job_class)
1980
2014
  self.assertEqual(self.local_job.job_class.description, "Validate job import")
1981
2015
 
@@ -3495,31 +3529,15 @@ class StatusTest(ModelTestCases.BaseModelTestCase):
3495
3529
  self.status.save()
3496
3530
  self.assertEqual(str(self.status), test)
3497
3531
 
3498
- @isolate_apps("nautobot.extras.tests")
3499
- def test_deprecated_mixin_class(self):
3500
- """Test that inheriting from StatusModel raises a DeprecationWarning."""
3501
- with warnings.catch_warnings(record=True) as warn_list:
3502
- warnings.simplefilter("always")
3503
-
3504
- class MyModel(StatusModel): # pylint: disable=unused-variable
3505
- pass
3506
-
3507
- self.assertEqual(len(warn_list), 1)
3508
- warning = warn_list[0]
3509
- self.assertTrue(issubclass(warning.category, DeprecationWarning))
3510
- self.assertIn("StatusModel is deprecated", str(warning))
3511
- self.assertIn("Instead of deriving MyModel from StatusModel", str(warning))
3512
- self.assertIn("please directly declare `status = StatusField(...)` on your model instead", str(warning))
3513
-
3514
3532
 
3515
3533
  class TagTest(ModelTestCases.BaseModelTestCase):
3516
3534
  model = Tag
3517
3535
 
3518
3536
  def test_create_tag_unicode(self):
3519
- tag = Tag(name="Testing Unicode: 台灣")
3520
- tag.save()
3537
+ instance = Tag(name="Testing Unicode: 台灣")
3538
+ instance.save()
3521
3539
 
3522
- self.assertEqual(tag.name, "Testing Unicode: 台灣")
3540
+ self.assertEqual(instance.name, "Testing Unicode: 台灣")
3523
3541
 
3524
3542
 
3525
3543
  class JobLogEntryTest(TestCase): # TODO: change to BaseModelTestCase