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
@@ -1,10 +1,13 @@
1
1
  """Unit tests for data_validation views."""
2
2
 
3
- from unittest.mock import MagicMock, patch
4
-
3
+ from constance import config
4
+ from django.contrib.auth import get_user_model
5
5
  from django.contrib.contenttypes.models import ContentType
6
- from django.http.request import QueryDict
6
+ from django.core.cache import caches
7
+ from django.test import override_settings
8
+ from django.urls import reverse
7
9
 
10
+ from nautobot.core import settings
8
11
  from nautobot.core.testing import TestCase, ViewTestCases
9
12
  from nautobot.data_validation.models import (
10
13
  DataCompliance,
@@ -13,13 +16,17 @@ from nautobot.data_validation.models import (
13
16
  RequiredValidationRule,
14
17
  UniqueValidationRule,
15
18
  )
16
- from nautobot.data_validation.tables import DataComplianceTableTab
17
19
  from nautobot.data_validation.tests import ValidationRuleTestCaseMixin
18
- from nautobot.data_validation.tests.test_data_compliance_rules import TestFailedDataComplianceRule
19
- from nautobot.data_validation.views import DataComplianceObjectView
20
+ from nautobot.data_validation.tests.test_data_compliance_rules import (
21
+ TestFailedDataComplianceRule,
22
+ TestFailedDataComplianceRuleAlt,
23
+ )
24
+ from nautobot.dcim.choices import DeviceUniquenessChoices
20
25
  from nautobot.dcim.models import Device, Location, LocationType, PowerFeed
21
26
  from nautobot.extras.models import Status
22
27
 
28
+ User = get_user_model()
29
+
23
30
 
24
31
  class RegularExpressionValidationRuleTestCase(ValidationRuleTestCaseMixin, ViewTestCases.PrimaryObjectViewTestCase):
25
32
  """View test cases for the RegularExpressionValidationRule model."""
@@ -247,31 +254,182 @@ class DataComplianceObjectTestCase(TestCase):
247
254
  """Test cases for DataComplianceObjectView."""
248
255
 
249
256
  def setUp(self):
250
- location_type = LocationType(name="Region")
251
- location_type.validated_save()
252
- s = Location(
253
- name="Test Location 1",
254
- location_type=LocationType.objects.get_by_natural_key("Region"),
255
- status=Status.objects.get_by_natural_key("Active"),
256
- )
257
- s.save()
258
- t = TestFailedDataComplianceRule(s)
257
+ self.device = Device.objects.first()
258
+
259
+ t = TestFailedDataComplianceRuleAlt(self.device)
259
260
  t.clean()
261
+ self.user = User.objects.create_user(username="testuser", is_superuser=True)
262
+
263
+ def test_data_compliance_action(self):
264
+ self.add_permissions("data_validation.view_datacompliance")
265
+ self.client.force_login(self.user)
266
+ url = reverse("dcim:device_data-compliance", kwargs={"pk": self.device.pk})
267
+ response = self.client.get(url)
268
+ self.assertEqual(response.status_code, 200)
269
+ self.assertIn("active_tab", response.context)
270
+ self.assertEqual(response.context["active_tab"], "data_compliance")
271
+ self.assertBodyContains(response, "The tenant is wrong")
272
+ self.assertBodyContains(response, "The name is wrong")
273
+ self.assertBodyContains(response, "The status is wrong")
274
+
275
+
276
+ class DeviceConstraintsViewTest(TestCase):
277
+ """Tests for the DeviceConstraintsView."""
260
278
 
261
- def test_get_extra_context(self):
262
- view = DataComplianceObjectView()
263
- location = Location.objects.first()
264
- mock_request = MagicMock()
265
- mock_request.GET = QueryDict("tab=data_validation:1")
266
- result = view.get_extra_context(mock_request, location)
267
- self.assertEqual(result["active_tab"], "data_validation:1")
268
- self.assertIsInstance(result["table"], DataComplianceTableTab)
269
-
270
- @patch("nautobot.core.views.generic.ObjectView.dispatch")
271
- def test_dispatch(self, mocked_dispatch):
272
- view = DataComplianceObjectView()
273
- mock_request = MagicMock()
274
- kwargs = {"model": "dcim.location", "other_arg": "other_arg", "another_arg": "another_arg"}
275
- view.dispatch(mock_request, **kwargs)
276
- mocked_dispatch.assert_called()
277
- mocked_dispatch.assert_called_with(mock_request, other_arg="other_arg", another_arg="another_arg")
279
+ def setUp(self):
280
+ self.initial_setting = config.DEVICE_UNIQUENESS
281
+ self.url = reverse("data_validation:device-constraints")
282
+ self.device_ct = ContentType.objects.get_for_model(Device)
283
+
284
+ def tearDown(self):
285
+ """Reset Constance config and clear cache."""
286
+ config.DEVICE_UNIQUENESS = self.initial_setting
287
+ cache = caches[settings.CONSTANCE_DATABASE_CACHE_BACKEND]
288
+ cache.clear()
289
+
290
+ @override_settings(
291
+ DEVICE_UNIQUENESS=DeviceUniquenessChoices.NONE,
292
+ )
293
+ def test_page_reflects_correct_settings_for_non_staff(self):
294
+ """Non-staff view still shows the actual configuration, but in disabled form."""
295
+ user = get_user_model().objects.create_user(username="testuser")
296
+ self.client.force_login(user)
297
+ response = self.client.get(self.url)
298
+
299
+ self.assertEqual(response.status_code, 200)
300
+
301
+ # Ensure fields are disabled but reflect correct config values
302
+ self.assertContains(response, f'value="{DeviceUniquenessChoices.NONE}" selected')
303
+ self.assertContains(response, "disabled")
304
+ self.assertContains(response, "You do not have permission to modify these settings.")
305
+
306
+ # DEVICE_NAME_REQUIRED should NOT be checked, because RequiredValidationRule not exist
307
+ device_ct = ContentType.objects.get_for_model(Device)
308
+ self.assertFalse(RequiredValidationRule.objects.filter(content_type=device_ct, field="name").exists())
309
+ self.assertNotContains(response, 'name="DEVICE_NAME_REQUIRED" checked')
310
+
311
+ # Footer buttons should NOT be rendered
312
+ self.assertNotContains(response, '<button type="submit"')
313
+ self.assertNotContains(response, "-->Update")
314
+ self.assertNotContains(response, "-->Cancel")
315
+
316
+ @override_settings(
317
+ DEVICE_UNIQUENESS=DeviceUniquenessChoices.NAME,
318
+ )
319
+ def test_page_reflects_correct_settings_for_staff(self):
320
+ """Page should reflect the true values of DEVICE_UNIQUENESS and DEVICE_NAME_REQUIRED."""
321
+ # Create RequiredValidationRule to check proper value of DEVICE_NAME_REQUIRED
322
+ device_ct = ContentType.objects.get_for_model(Device)
323
+ RequiredValidationRule.objects.create(
324
+ name="Required Name rule",
325
+ content_type=device_ct,
326
+ field="name",
327
+ )
328
+ user = get_user_model().objects.create_user(username="testuser", is_staff=True)
329
+ self.client.force_login(user)
330
+ response = self.client.get(self.url)
331
+
332
+ self.assertEqual(response.status_code, 200)
333
+ self.assertTemplateUsed(response, "data_validation/device_constraints.html")
334
+
335
+ self.assertIn("form", response.context)
336
+ self.assertContains(response, "Device Constraints")
337
+
338
+ # Check that the correct DEVICE_UNIQUENESS option is selected
339
+ self.assertContains(response, f'value="{DeviceUniquenessChoices.NAME}" selected')
340
+
341
+ self.assertNotContains(response, "disabled")
342
+ self.assertNotContains(response, "You do not have permission to modify these settings.")
343
+
344
+ # Check that DEVICE_NAME_REQUIRED checkbox is checked when RequiredValidationRule exist
345
+ self.assertTrue(RequiredValidationRule.objects.filter(content_type=device_ct, field="name").exists())
346
+ self.assertContains(response, 'id="id_DEVICE_NAME_REQUIRED" checked')
347
+
348
+ # Footer buttons should be rendered
349
+ self.assertContains(response, '<button type="submit"')
350
+ self.assertContains(response, "-->Update")
351
+ self.assertContains(response, "-->Cancel")
352
+
353
+ def test_post_as_non_admin_denied(self):
354
+ """POST by non-admin should be denied."""
355
+ user = get_user_model().objects.create_user(username="normaluser")
356
+ self.client.force_login(user)
357
+
358
+ response = self.client.post(
359
+ self.url,
360
+ data={
361
+ "DEVICE_UNIQUENESS": DeviceUniquenessChoices.LOCATION_TENANT_NAME,
362
+ "DEVICE_NAME_REQUIRED": True,
363
+ },
364
+ follow=True,
365
+ )
366
+ self.assertEqual(response.status_code, 403)
367
+
368
+ # No rule should be created
369
+ self.assertFalse(RequiredValidationRule.objects.filter(content_type=self.device_ct, field="name").exists())
370
+ self.assertEqual(config.DEVICE_UNIQUENESS, self.initial_setting)
371
+
372
+ def test_post_updates_device_uniqueness_and_creates_required_rule(self):
373
+ """POST with DEVICE_NAME_REQUIRED=True should create a RequiredValidationRule."""
374
+ user = get_user_model().objects.create_user(username="testuser", is_staff=True)
375
+ self.client.force_login(user)
376
+ response = self.client.post(
377
+ self.url,
378
+ {
379
+ "DEVICE_UNIQUENESS": DeviceUniquenessChoices.NAME,
380
+ "DEVICE_NAME_REQUIRED": True,
381
+ },
382
+ follow=True,
383
+ )
384
+
385
+ self.assertRedirects(response, self.url)
386
+ self.assertEqual(config.DEVICE_UNIQUENESS, "name")
387
+
388
+ rule_exists = RequiredValidationRule.objects.filter(
389
+ content_type=self.device_ct,
390
+ field="name",
391
+ ).exists()
392
+ self.assertTrue(rule_exists)
393
+
394
+ def test_post_disables_required_rule(self):
395
+ """POST with DEVICE_NAME_REQUIRED=False should delete the RequiredValidationRule."""
396
+ user = get_user_model().objects.create_user(username="testuser", is_staff=True)
397
+ self.client.force_login(user)
398
+ RequiredValidationRule.objects.create(
399
+ name="Required Name rule",
400
+ content_type=self.device_ct,
401
+ field="name",
402
+ )
403
+ self.assertTrue(RequiredValidationRule.objects.filter(content_type=self.device_ct, field="name").exists())
404
+
405
+ response = self.client.post(
406
+ self.url,
407
+ {
408
+ "DEVICE_UNIQUENESS": DeviceUniquenessChoices.LOCATION_TENANT_NAME,
409
+ "DEVICE_NAME_REQUIRED": False,
410
+ },
411
+ follow=True,
412
+ )
413
+ self.assertRedirects(response, self.url)
414
+ self.assertEqual(config.DEVICE_UNIQUENESS, DeviceUniquenessChoices.LOCATION_TENANT_NAME)
415
+
416
+ self.assertFalse(RequiredValidationRule.objects.filter(content_type=self.device_ct, field="name").exists())
417
+
418
+ def test_invalid_post_rerenders_form(self):
419
+ """If form is invalid, the view should re-render without redirect for multiple invalid inputs."""
420
+ user = get_user_model().objects.create_user(username="testuser", is_staff=True)
421
+ self.client.force_login(user)
422
+
423
+ invalid_inputs = [
424
+ {"DEVICE_UNIQUENESS": ""},
425
+ {"DEVICE_UNIQUENESS": "invalid_value"},
426
+ ]
427
+
428
+ for post_data in invalid_inputs:
429
+ with self.subTest(post_data=post_data):
430
+ response = self.client.post(self.url, post_data)
431
+ self.assertEqual(response.status_code, 200)
432
+ self.assertTemplateUsed(response, "data_validation/device_constraints.html")
433
+ self.assertIn("form", response.context)
434
+ self.assertTrue(response.context["form"].errors)
435
+ self.assertEqual(config.DEVICE_UNIQUENESS, self.initial_setting)
@@ -15,10 +15,7 @@ router.register("required-rules", views.RequiredValidationRuleUIViewSet)
15
15
  router.register("unique-rules", views.UniqueValidationRuleUIViewSet)
16
16
 
17
17
  urlpatterns = [
18
- path(
19
- "data-compliance/<model>/<uuid:id>/",
20
- views.DataComplianceObjectView.as_view(),
21
- name="data-compliance-tab",
22
- ),
18
+ path("device-constraints/", views.DeviceConstraintsView.as_view(), name="device-constraints"),
23
19
  ]
20
+
24
21
  urlpatterns += router.urls
@@ -1,15 +1,17 @@
1
1
  """Views for data_validation."""
2
2
 
3
- from django.apps import apps as global_apps
3
+ from constance import config
4
+ from django.contrib import messages
4
5
  from django.contrib.contenttypes.models import ContentType
5
- from django_tables2 import RequestConfig
6
+ from django.shortcuts import redirect, render
6
7
 
8
+ from nautobot.apps.ui import Breadcrumbs, Titles, ViewNameBreadcrumbItem
7
9
  from nautobot.core.ui.choices import SectionChoices
8
10
  from nautobot.core.ui.object_detail import (
9
11
  ObjectDetailContent,
10
12
  ObjectFieldsPanel,
11
13
  )
12
- from nautobot.core.views.generic import ObjectView
14
+ from nautobot.core.views.generic import GenericView
13
15
  from nautobot.core.views.mixins import (
14
16
  ObjectBulkDestroyViewMixin,
15
17
  ObjectChangeLogViewMixin,
@@ -18,18 +20,10 @@ from nautobot.core.views.mixins import (
18
20
  ObjectListViewMixin,
19
21
  ObjectNotesViewMixin,
20
22
  )
21
- from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
22
23
  from nautobot.core.views.viewsets import NautobotUIViewSet
23
- from nautobot.data_validation import filters, forms, tables
24
+ from nautobot.data_validation import filters, forms, models, tables
24
25
  from nautobot.data_validation.api import serializers
25
- from nautobot.data_validation.models import (
26
- DataCompliance,
27
- MinMaxValidationRule,
28
- RegularExpressionValidationRule,
29
- RequiredValidationRule,
30
- UniqueValidationRule,
31
- )
32
- from nautobot.extras.utils import get_base_template
26
+ from nautobot.dcim.models import Device
33
27
 
34
28
  #
35
29
  # RegularExpressionValidationRules
@@ -43,7 +37,7 @@ class RegularExpressionValidationRuleUIViewSet(NautobotUIViewSet):
43
37
  filterset_class = filters.RegularExpressionValidationRuleFilterSet
44
38
  filterset_form_class = forms.RegularExpressionValidationRuleFilterForm
45
39
  form_class = forms.RegularExpressionValidationRuleForm
46
- queryset = RegularExpressionValidationRule.objects.all()
40
+ queryset = models.RegularExpressionValidationRule.objects.all()
47
41
  serializer_class = serializers.RegularExpressionValidationRuleSerializer
48
42
  table_class = tables.RegularExpressionValidationRuleTable
49
43
  object_detail_content = ObjectDetailContent(
@@ -69,7 +63,7 @@ class MinMaxValidationRuleUIViewSet(NautobotUIViewSet):
69
63
  filterset_class = filters.MinMaxValidationRuleFilterSet
70
64
  filterset_form_class = forms.MinMaxValidationRuleFilterForm
71
65
  form_class = forms.MinMaxValidationRuleForm
72
- queryset = MinMaxValidationRule.objects.all()
66
+ queryset = models.MinMaxValidationRule.objects.all()
73
67
  serializer_class = serializers.MinMaxValidationRuleSerializer
74
68
  table_class = tables.MinMaxValidationRuleTable
75
69
  object_detail_content = ObjectDetailContent(
@@ -95,7 +89,7 @@ class RequiredValidationRuleUIViewSet(NautobotUIViewSet):
95
89
  filterset_class = filters.RequiredValidationRuleFilterSet
96
90
  filterset_form_class = forms.RequiredValidationRuleFilterForm
97
91
  form_class = forms.RequiredValidationRuleForm
98
- queryset = RequiredValidationRule.objects.all()
92
+ queryset = models.RequiredValidationRule.objects.all()
99
93
  serializer_class = serializers.RequiredValidationRuleSerializer
100
94
  table_class = tables.RequiredValidationRuleTable
101
95
  object_detail_content = ObjectDetailContent(
@@ -121,7 +115,7 @@ class UniqueValidationRuleUIViewSet(NautobotUIViewSet):
121
115
  filterset_class = filters.UniqueValidationRuleFilterSet
122
116
  filterset_form_class = forms.UniqueValidationRuleFilterForm
123
117
  form_class = forms.UniqueValidationRuleForm
124
- queryset = UniqueValidationRule.objects.all()
118
+ queryset = models.UniqueValidationRule.objects.all()
125
119
  serializer_class = serializers.UniqueValidationRuleSerializer
126
120
  table_class = tables.UniqueValidationRuleTable
127
121
  object_detail_content = ObjectDetailContent(
@@ -151,7 +145,7 @@ class DataComplianceUIViewSet( # pylint: disable=W0223
151
145
  """Views for the DataComplianceUIViewSet model."""
152
146
 
153
147
  lookup_field = "pk"
154
- queryset = DataCompliance.objects.all()
148
+ queryset = models.DataCompliance.objects.all()
155
149
  table_class = tables.DataComplianceTable
156
150
  filterset_class = filters.DataComplianceFilterSet
157
151
  filterset_form_class = forms.DataComplianceFilterForm
@@ -162,33 +156,72 @@ class DataComplianceUIViewSet( # pylint: disable=W0223
162
156
  ObjectFieldsPanel(
163
157
  section=SectionChoices.LEFT_HALF,
164
158
  weight=100,
165
- fields="__all__",
159
+ fields=[
160
+ "content_type",
161
+ "compliance_class_name",
162
+ "last_validation_date",
163
+ "validated_object",
164
+ "validated_attribute",
165
+ "validated_attribute_value",
166
+ "valid",
167
+ "message",
168
+ ],
166
169
  ),
167
170
  )
168
171
  )
169
172
 
170
173
 
171
- class DataComplianceObjectView(ObjectView):
172
- """View for the Audit Results tab dynamically generated on specific object detail views."""
173
-
174
- template_name = "data_validation/datacompliance_tab.html"
175
- queryset = None
176
-
177
- def dispatch(self, request, *args, **kwargs):
178
- """Set the queryset for the given object and call the inherited dispatch method."""
179
- model = kwargs.pop("model")
180
- if not self.queryset:
181
- self.queryset = global_apps.get_model(model).objects.all()
182
- return super().dispatch(request, *args, **kwargs)
174
+ class DeviceConstraintsView(GenericView):
175
+ template_name = "data_validation/device_constraints.html"
176
+ view_titles = Titles(titles={"*": "Device Constraints"})
177
+ breadcrumbs = Breadcrumbs(
178
+ items={
179
+ "*": [
180
+ ViewNameBreadcrumbItem(view_name="data_validation:device-constraints", label="Device Constraints"),
181
+ ],
182
+ },
183
+ )
183
184
 
184
- def get_extra_context(self, request, instance):
185
- """Generate extra context for rendering the DataComplianceObjectView template."""
186
- compliance_objects = DataCompliance.objects.filter(
187
- content_type=ContentType.objects.get_for_model(instance), object_id=instance.id
185
+ def get(self, request):
186
+ form = forms.DeviceConstraintsForm(user=request.user)
187
+ return render(
188
+ request,
189
+ self.template_name,
190
+ {
191
+ "form": form,
192
+ "view_titles": self.get_view_titles(),
193
+ "breadcrumbs": self.get_breadcrumbs(),
194
+ },
188
195
  )
189
- compliance_table = tables.DataComplianceTableTab(compliance_objects)
190
- base_template = get_base_template(None, instance)
191
196
 
192
- paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
193
- RequestConfig(request, paginate).configure(compliance_table)
194
- return {"active_tab": request.GET["tab"], "table": compliance_table, "base_template": base_template}
197
+ def post(self, request):
198
+ if not request.user.is_staff:
199
+ return self.handle_no_permission()
200
+ form = forms.DeviceConstraintsForm(request.POST)
201
+ if form.is_valid():
202
+ config.DEVICE_UNIQUENESS = form.cleaned_data["DEVICE_UNIQUENESS"]
203
+ device_ct = ContentType.objects.get_for_model(Device)
204
+ if form.cleaned_data["DEVICE_NAME_REQUIRED"]:
205
+ models.RequiredValidationRule.objects.get_or_create(
206
+ content_type=device_ct,
207
+ field="name",
208
+ defaults={"name": "Require Device Name"},
209
+ )
210
+ else:
211
+ models.RequiredValidationRule.objects.filter(
212
+ content_type=device_ct,
213
+ field="name",
214
+ ).delete()
215
+
216
+ messages.success(request, "Device constraints have been updated successfully.")
217
+ return redirect("data_validation:device-constraints")
218
+
219
+ return render(
220
+ request,
221
+ self.template_name,
222
+ {
223
+ "form": form,
224
+ "view_titles": self.get_view_titles(),
225
+ "breadcrumbs": self.get_breadcrumbs(),
226
+ },
227
+ )
@@ -23,7 +23,6 @@ from nautobot.core.api.utils import (
23
23
  )
24
24
  from nautobot.core.models.utils import get_all_concrete_models
25
25
  from nautobot.core.utils.config import get_settings_or_config
26
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
27
26
  from nautobot.dcim.choices import (
28
27
  CableLengthUnitChoices,
29
28
  CableTypeChoices,
@@ -31,6 +30,7 @@ from nautobot.dcim.choices import (
31
30
  ControllerCapabilitiesChoices,
32
31
  DeviceFaceChoices,
33
32
  DeviceRedundancyGroupFailoverStrategyChoices,
33
+ InterfaceDuplexChoices,
34
34
  InterfaceModeChoices,
35
35
  InterfaceRedundancyGroupProtocolChoices,
36
36
  InterfaceTypeChoices,
@@ -137,12 +137,6 @@ class CableTerminationModelSerializerMixin(serializers.ModelSerializer):
137
137
  return None
138
138
 
139
139
 
140
- # TODO: remove in 2.2
141
- @class_deprecated_in_favor_of(CableTerminationModelSerializerMixin)
142
- class CableTerminationSerializer(CableTerminationModelSerializerMixin):
143
- pass
144
-
145
-
146
140
  class PathEndpointModelSerializerMixin(ValidatedModelSerializer):
147
141
  connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
148
142
  connected_endpoint = serializers.SerializerMethodField(read_only=True)
@@ -183,12 +177,6 @@ class PathEndpointModelSerializerMixin(ValidatedModelSerializer):
183
177
  return None
184
178
 
185
179
 
186
- # TODO: remove in 2.2
187
- @class_deprecated_in_favor_of(PathEndpointModelSerializerMixin)
188
- class ConnectedEndpointSerializer(PathEndpointModelSerializerMixin):
189
- pass
190
-
191
-
192
180
  class ModularDeviceComponentTemplateSerializerMixin:
193
181
  def validate(self, data):
194
182
  """Validate device_type and module_type field constraints for modular device component templates."""
@@ -706,6 +694,8 @@ class InterfaceSerializer(
706
694
  mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
707
695
  mac_address = serializers.CharField(allow_blank=True, allow_null=True, required=False)
708
696
  ip_address_count = serializers.IntegerField(read_only=True, source="_ip_address_count")
697
+ speed = serializers.IntegerField(required=False, allow_null=True)
698
+ duplex = ChoiceField(choices=InterfaceDuplexChoices, allow_blank=True, required=False)
709
699
 
710
700
  class Meta:
711
701
  model = Interface
nautobot/dcim/apps.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from nautobot.core.apps import NautobotConfig
2
+ from nautobot.extras.plugins import register_custom_validators
2
3
 
3
4
 
4
5
  class DCIMConfig(NautobotConfig):
@@ -26,4 +27,7 @@ class DCIMConfig(NautobotConfig):
26
27
 
27
28
  def ready(self):
28
29
  super().ready()
30
+ from nautobot.dcim.custom_validators import custom_validators
31
+
32
+ register_custom_validators(custom_validators)
29
33
  import nautobot.dcim.signals # noqa: F401 # unused-import -- but this import installs the signals
nautobot/dcim/choices.py CHANGED
@@ -159,6 +159,20 @@ class DeviceStatusChoices(ChoiceSet):
159
159
  )
160
160
 
161
161
 
162
+ class DeviceUniquenessChoices(ChoiceSet):
163
+ LOCATION_TENANT_NAME = "location_tenant_name"
164
+ NAME = "name"
165
+ NONE = "none"
166
+
167
+ DEFAULT = LOCATION_TENANT_NAME
168
+
169
+ CHOICES = [
170
+ (LOCATION_TENANT_NAME, "Location + Tenant + Name"),
171
+ (NAME, "Device name must be globally unique"),
172
+ (NONE, "No enforced uniqueness"),
173
+ ]
174
+
175
+
162
176
  #
163
177
  # ConsolePorts
164
178
  #
@@ -764,6 +778,7 @@ class InterfaceTypeChoices(ChoiceSet):
764
778
  TYPE_VIRTUAL = "virtual"
765
779
  TYPE_BRIDGE = "bridge"
766
780
  TYPE_LAG = "lag"
781
+ TYPE_TUNNEL = "tunnel"
767
782
 
768
783
  # Ethernet
769
784
  TYPE_100ME_FX = "100base-fx"
@@ -932,6 +947,7 @@ class InterfaceTypeChoices(ChoiceSet):
932
947
  (TYPE_VIRTUAL, "Virtual"),
933
948
  (TYPE_BRIDGE, "Bridge"),
934
949
  (TYPE_LAG, "Link Aggregation Group (LAG)"),
950
+ (TYPE_TUNNEL, "Tunnel"),
935
951
  ),
936
952
  ),
937
953
  (
@@ -1138,6 +1154,55 @@ class InterfaceModeChoices(ChoiceSet):
1138
1154
  )
1139
1155
 
1140
1156
 
1157
+ class InterfaceDuplexChoices(ChoiceSet):
1158
+ DUPLEX_AUTO = "auto"
1159
+ DUPLEX_FULL = "full"
1160
+ DUPLEX_HALF = "half"
1161
+
1162
+ CHOICES = (
1163
+ (DUPLEX_AUTO, "Auto"),
1164
+ (DUPLEX_FULL, "Full"),
1165
+ (DUPLEX_HALF, "Half"),
1166
+ )
1167
+
1168
+
1169
+ class InterfaceSpeedChoices(ChoiceSet):
1170
+ # Stored in Kbps (for compatibility with circuits and humanize_speed filter)
1171
+ SPEED_1M = 1_000
1172
+ SPEED_10M = 10_000
1173
+ SPEED_100M = 100_000
1174
+ SPEED_1G = 1_000_000
1175
+ SPEED_2_5G = 2_500_000
1176
+ SPEED_5G = 5_000_000
1177
+ SPEED_10G = 10_000_000
1178
+ SPEED_25G = 25_000_000
1179
+ SPEED_40G = 40_000_000
1180
+ SPEED_50G = 50_000_000
1181
+ SPEED_100G = 100_000_000
1182
+ SPEED_200G = 200_000_000
1183
+ SPEED_400G = 400_000_000
1184
+ SPEED_800G = 800_000_000
1185
+ SPEED_1_6T = 1_600_000_000
1186
+
1187
+ CHOICES = (
1188
+ (SPEED_1M, "1 Mbps"),
1189
+ (SPEED_10M, "10 Mbps"),
1190
+ (SPEED_100M, "100 Mbps"),
1191
+ (SPEED_1G, "1 Gbps"),
1192
+ (SPEED_2_5G, "2.5 Gbps"),
1193
+ (SPEED_5G, "5 Gbps"),
1194
+ (SPEED_10G, "10 Gbps"),
1195
+ (SPEED_25G, "25 Gbps"),
1196
+ (SPEED_40G, "40 Gbps"),
1197
+ (SPEED_50G, "50 Gbps"),
1198
+ (SPEED_100G, "100 Gbps"),
1199
+ (SPEED_200G, "200 Gbps"),
1200
+ (SPEED_400G, "400 Gbps"),
1201
+ (SPEED_800G, "800 Gbps"),
1202
+ (SPEED_1_6T, "1.6 Tbps"),
1203
+ )
1204
+
1205
+
1141
1206
  class InterfaceStatusChoices(ChoiceSet):
1142
1207
  STATUS_PLANNED = "planned"
1143
1208
  STATUS_ACTIVE = "active"
@@ -37,6 +37,13 @@ VIRTUAL_IFACE_TYPES = interface_type_by_category["Virtual interfaces"]
37
37
 
38
38
  NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES
39
39
 
40
+ COPPER_TWISTED_PAIR_IFACE_TYPES = [
41
+ InterfaceTypeChoices.TYPE_100ME_FIXED,
42
+ InterfaceTypeChoices.TYPE_1GE_FIXED,
43
+ InterfaceTypeChoices.TYPE_2GE_FIXED,
44
+ InterfaceTypeChoices.TYPE_5GE_FIXED,
45
+ InterfaceTypeChoices.TYPE_10GE_FIXED,
46
+ ]
40
47
 
41
48
  #
42
49
  # PowerFeeds