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
@@ -40,6 +40,7 @@ from nautobot.ipam.models import (
40
40
  VRFDeviceAssignment,
41
41
  VRFPrefixAssignment,
42
42
  )
43
+ from nautobot.tenancy.models import Tenant
43
44
  from nautobot.virtualization.models import Cluster, ClusterType, VirtualMachine, VMInterface
44
45
 
45
46
 
@@ -57,22 +58,28 @@ class NamespaceTest(APIViewTestCases.APIViewTestCase):
57
58
  @classmethod
58
59
  def setUpTestData(cls):
59
60
  location = Location.objects.first()
61
+ tenant = Tenant.objects.first()
60
62
  cls.create_data = [
61
63
  {
62
64
  "name": "Purple Monkey Namespace 1",
63
- "description": "A perfectly cromulent namespace.",
65
+ "description": "A namespace with a tenant and location.",
64
66
  "location": location.pk,
67
+ "tenant": tenant.pk,
65
68
  },
66
69
  {
67
70
  "name": "Purple Monkey Namespace 2",
68
- "description": "A secondarily cromulent namespace.",
69
- "location": location.pk,
71
+ "description": "A namespace with a tenant and no location.",
72
+ "tenant": tenant.pk,
70
73
  },
71
74
  {
72
75
  "name": "Purple Monkey Namespace 3",
73
- "description": "A third cromulent namespace.",
76
+ "description": "A namespace with no tenant but with a location.",
74
77
  "location": location.pk,
75
78
  },
79
+ {
80
+ "name": "Purple Monkey Namespace 4",
81
+ "description": "A namespace with no tenant and no location.",
82
+ },
76
83
  ]
77
84
  cls.bulk_update_data = {
78
85
  "description": "A perfectly new description.",
@@ -194,7 +201,7 @@ class VRFDeviceAssignmentTest(APIViewTestCases.APIViewTestCase):
194
201
  },
195
202
  {
196
203
  "vrf": cls.vrfs[4].pk,
197
- "virtual_device_context": cls.vdcs[0].pk,
204
+ "virtual_device_context": cls.vdcs[1].pk,
198
205
  },
199
206
  ]
200
207
  cls.bulk_update_data = {
@@ -1619,7 +1626,7 @@ class IPAddressTest(APIViewTestCases.APIViewTestCase):
1619
1626
  self.assertHttpStatus(ip2, status.HTTP_201_CREATED)
1620
1627
 
1621
1628
  response = self.client.get(
1622
- self._get_detail_url(nat_inside) + "?depth=1",
1629
+ self._get_detail_url(nat_inside) + "?depth=1&exclude_m2m=false",
1623
1630
  **self.header,
1624
1631
  )
1625
1632
  self.assertHttpStatus(response, status.HTTP_200_OK)
@@ -17,6 +17,7 @@ from nautobot.ipam.choices import PrefixTypeChoices, ServiceProtocolChoices
17
17
  from nautobot.ipam.filters import (
18
18
  IPAddressFilterSet,
19
19
  IPAddressToInterfaceFilterSet,
20
+ NamespaceFilterSet,
20
21
  PrefixFilterSet,
21
22
  PrefixLocationAssignmentFilterSet,
22
23
  RIRFilterSet,
@@ -54,6 +55,15 @@ from nautobot.virtualization.models import (
54
55
  )
55
56
 
56
57
 
58
+ class NamespaceTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilterTestCaseMixin):
59
+ """Namespace FilterSet tests"""
60
+
61
+ queryset = Namespace.objects.all()
62
+ filterset = NamespaceFilterSet
63
+ tenancy_related_name = "namespaces"
64
+ generic_filter_tests = (("name",),)
65
+
66
+
57
67
  class VRFTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilterTestCaseMixin):
58
68
  """VRF Filterset tests
59
69
 
@@ -177,6 +187,7 @@ class PrefixTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilt
177
187
  ["status", "status__id"],
178
188
  ["status", "status__name"],
179
189
  ["type"],
190
+ ["vpn_tunnel_endpoints", "vpn_tunnel_endpoints__id"],
180
191
  )
181
192
 
182
193
  def test_filters_generic(self):
@@ -220,6 +231,17 @@ class PrefixTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilt
220
231
  all_prefixes = self.queryset.all()
221
232
  self.assertQuerysetEqualAndNotEmpty(self.filterset(params, self.queryset).qs, all_prefixes)
222
233
 
234
+ def test_ancestors(self):
235
+ prefixes = (
236
+ Prefix.objects.create(prefix="10.0.0.0/8", status=Status.objects.get_for_model(Prefix).first()),
237
+ Prefix.objects.create(prefix="10.0.0.0/16", status=Status.objects.get_for_model(Prefix).first()),
238
+ Prefix.objects.create(prefix="10.0.0.0/24", status=Status.objects.get_for_model(Prefix).first()),
239
+ )
240
+ params = {"ancestors": [str(prefixes[2].pk)]}
241
+ filterset = self.filterset(params, self.queryset)
242
+ ancestors = [ancestor.id for ancestor in prefixes[2].ancestors()]
243
+ self.assertQuerysetEqualAndNotEmpty(filterset.qs, self.queryset.filter(id__in=ancestors))
244
+
223
245
 
224
246
  class PrefixLocationAssignmentTestCase(FilterTestCases.FilterTestCase):
225
247
  queryset = PrefixLocationAssignment.objects.all()
@@ -508,7 +530,11 @@ class IPAddressTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyF
508
530
  queryset = IPAddress.objects.all()
509
531
  filterset = IPAddressFilterSet
510
532
  tenancy_related_name = "ip_addresses"
511
- generic_filter_tests = (["nat_inside", "nat_inside__id"],)
533
+ generic_filter_tests = (
534
+ ["nat_inside", "nat_inside__id"],
535
+ ["services", "services__id"],
536
+ ["services", "services__name"],
537
+ )
512
538
 
513
539
  @classmethod
514
540
  def setUpTestData(cls):
@@ -700,6 +726,15 @@ class IPAddressTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyF
700
726
  nat_inside=ip1,
701
727
  )
702
728
 
729
+ services = (
730
+ Service.objects.create(name="Service 1", protocol="TCP", ports=[80]),
731
+ Service.objects.create(name="Service 2", protocol="UDP", ports=[53]),
732
+ Service.objects.create(name="Service 3", protocol="TCP", ports=[443]),
733
+ )
734
+ services[0].ip_addresses.add(ip0)
735
+ services[1].ip_addresses.add(ip1)
736
+ services[2].ip_addresses.add(ip2)
737
+
703
738
  def test_search(self):
704
739
  ipv4_octets = self.ipv4_address.host.split(".")
705
740
  ipv6_hextets = self.ipv6_address.host.split(":")
@@ -1,8 +1,8 @@
1
1
  """Test IPAM forms."""
2
2
 
3
3
  from django.forms import Form
4
- from django.test import TestCase
5
4
 
5
+ from nautobot.core.testing import TestCase
6
6
  from nautobot.core.testing.forms import FormTestCases
7
7
  from nautobot.extras.models import Status
8
8
  from nautobot.ipam import forms, models
@@ -5,9 +5,9 @@ from django.contrib.contenttypes.models import ContentType
5
5
  from django.core.exceptions import ValidationError
6
6
  from django.db import connection, IntegrityError
7
7
  from django.db.models import ProtectedError
8
- from django.test import TestCase
9
8
  import netaddr
10
9
 
10
+ from nautobot.core.testing import TestCase
11
11
  from nautobot.core.testing.models import ModelTestCases
12
12
  from nautobot.dcim import choices as dcim_choices
13
13
  from nautobot.dcim.models import Device, DeviceType, Interface, Location, LocationType, Module, ModuleBay, ModuleType
@@ -34,7 +34,7 @@ class IPAddressToInterfaceTest(TestCase):
34
34
 
35
35
  @classmethod
36
36
  def setUpTestData(cls):
37
- cls.namespace = Namespace.objects.first()
37
+ cls.namespace = Namespace.objects.create(name="IPAM Models Test Namespace")
38
38
  cls.status = Status.objects.get(name="Active")
39
39
  cls.prefix = Prefix.objects.create(prefix="192.0.2.0/24", status=cls.status, namespace=cls.namespace)
40
40
  cls.test_device = Device.objects.create(
@@ -172,6 +172,48 @@ class IPAddressToInterfaceTest(TestCase):
172
172
  IPAddressToInterface.objects.create(vm_interface=None, interface=None, ip_address=ip_addr)
173
173
  self.assertIn("Must associate to either an Interface or a VMInterface.", str(cm.exception))
174
174
 
175
+ def test_m2m_save_signal_prevents_iface_ipaddress_and_vminterface_through_defaults(self):
176
+ """Test that the m2m save signal prevents a VMInterface from using the same IPAddressToInterface instance as an Interface."""
177
+ ip_addr = IPAddress.objects.create(address="192.0.2.1/24", status=self.status, namespace=self.namespace)
178
+ with self.assertRaises(ValidationError) as cm:
179
+ self.test_int1.ip_addresses.add(ip_addr, through_defaults={"vm_interface": self.test_vmint1})
180
+
181
+ self.assertIn(
182
+ "Cannot use a single instance to associate to both an Interface and a VMInterface.", str(cm.exception)
183
+ )
184
+
185
+ def test_m2m_save_signal_prevents_vminterface_ipaddress_and_interface_through_defaults(self):
186
+ """Test that the m2m save signal prevents an Interface from using the same IPAddressToInterface instance as a VMInterface."""
187
+ ip_addr = IPAddress.objects.create(address="192.0.2.1/24", status=self.status, namespace=self.namespace)
188
+ with self.assertRaises(ValidationError) as cm:
189
+ self.test_vmint1.ip_addresses.add(ip_addr, through_defaults={"interface": self.test_int1})
190
+
191
+ self.assertIn(
192
+ "Cannot use a single instance to associate to both an Interface and a VMInterface.", str(cm.exception)
193
+ )
194
+
195
+ def test_m2m_save_signal_prevents_ipaddress_interface_and_vminterface_through_defaults(self):
196
+ """Test that the m2m save signal prevents an Interface being added to an IPAddress with a VMInterface through_defaults."""
197
+ ip_addr = IPAddress.objects.create(address="192.0.2.1/24", status=self.status, namespace=self.namespace)
198
+
199
+ with self.assertRaises(ValidationError) as cm:
200
+ ip_addr.interfaces.add(self.test_int1, through_defaults={"vm_interface": self.test_vmint1})
201
+
202
+ self.assertIn(
203
+ "Cannot use a single instance to associate to both an Interface and a VMInterface.", str(cm.exception)
204
+ )
205
+
206
+ def test_m2m_save_signal_prevents_ipaddress_vminterface_and_interface_through_defaults(self):
207
+ """Test that the m2m save signal prevents a VMInterface being added to an IPAddress with an Interface through_defaults."""
208
+ ip_addr = IPAddress.objects.create(address="192.0.2.1/24", status=self.status, namespace=self.namespace)
209
+
210
+ with self.assertRaises(ValidationError) as cm:
211
+ ip_addr.vm_interfaces.add(self.test_vmint1, through_defaults={"interface": self.test_int1})
212
+
213
+ self.assertIn(
214
+ "Cannot use a single instance to associate to both an Interface and a VMInterface.", str(cm.exception)
215
+ )
216
+
175
217
  def test_primary_ip_retained_when_deleted_from_device_or_module_interface(self):
176
218
  """Test primary_ip4 remains set when the same IP is assigned to multiple interfaces and deleted from one."""
177
219
 
@@ -1,6 +1,5 @@
1
- from django.test import TestCase
2
-
3
1
  from nautobot.core.models.querysets import count_related
2
+ from nautobot.core.testing import TestCase
4
3
  from nautobot.dcim.models.locations import Location
5
4
  from nautobot.ipam.models import Prefix
6
5
  from nautobot.ipam.tables import PrefixTable
@@ -1,7 +1,7 @@
1
- from django.test import TestCase
2
1
  import netaddr
3
2
 
4
3
  from nautobot.core.forms.utils import parse_numeric_range
4
+ from nautobot.core.testing import TestCase
5
5
  from nautobot.extras.models import Status
6
6
  from nautobot.ipam.models import IPAddress, Namespace, Prefix, VLAN, VLANGroup
7
7
  from nautobot.ipam.utils import add_available_ipaddresses, add_available_vlans
@@ -51,17 +51,7 @@ from nautobot.users.models import ObjectPermission
51
51
  from nautobot.virtualization.models import Cluster, ClusterType, VirtualMachine
52
52
 
53
53
 
54
- class NamespaceTestCase(
55
- ViewTestCases.GetObjectViewTestCase,
56
- ViewTestCases.GetObjectChangelogViewTestCase,
57
- ViewTestCases.GetObjectNotesViewTestCase,
58
- ViewTestCases.CreateObjectViewTestCase,
59
- ViewTestCases.EditObjectViewTestCase,
60
- ViewTestCases.DeleteObjectViewTestCase,
61
- ViewTestCases.ListObjectsViewTestCase,
62
- ViewTestCases.BulkEditObjectsViewTestCase,
63
- ViewTestCases.BulkDeleteObjectsViewTestCase,
64
- ):
54
+ class NamespaceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
65
55
  model = Namespace
66
56
  custom_action_required_permissions = {
67
57
  "ipam:namespace_vrfs": ["ipam.view_namespace", "ipam.view_vrf"],
@@ -72,11 +62,18 @@ class NamespaceTestCase(
72
62
  @classmethod
73
63
  def setUpTestData(cls):
74
64
  locations = Location.objects.get_for_model(Namespace)
65
+ tenants = Tenant.objects.all()[:2]
75
66
 
76
- cls.form_data = {"name": "Namespace X", "location": locations[0].pk, "description": "A new Namespace"}
67
+ cls.form_data = {
68
+ "name": "Namespace X",
69
+ "location": locations[0].pk,
70
+ "tenant": tenants[0].pk,
71
+ "description": "A new Namespace",
72
+ }
77
73
 
78
74
  cls.bulk_edit_data = {
79
75
  "description": "New description",
76
+ "tenant": tenants[1].pk,
80
77
  "location": locations[1].pk,
81
78
  }
82
79
 
@@ -165,7 +162,7 @@ class RIRTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
165
162
  if count > 1:
166
163
  self.assertBodyContains(
167
164
  response,
168
- f'<a href="{prefix_list_url}?{urlencode({"rir": rir.name})}" class="badge">{count}</a>',
165
+ f'<a href="{prefix_list_url}?{urlencode({"rir": rir.name})}" class="badge bg-primary">{count}</a>',
169
166
  )
170
167
  elif count == 1:
171
168
  self.assertBodyContains(response, hyperlinked_object(rir.prefixes.first()))
@@ -250,7 +247,8 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
250
247
  count = prefix.locations.count()
251
248
  if count > 1:
252
249
  self.assertBodyContains(
253
- response, f'<a href="{locations_list_url}?prefixes={prefix.pk}" class="badge">{count}</a>'
250
+ response,
251
+ f'<a href="{locations_list_url}?prefixes={prefix.pk}" class="badge bg-primary">{count}</a>',
254
252
  )
255
253
  elif count == 1:
256
254
  self.assertBodyContains(response, hyperlinked_object(prefix.locations.first(), "name"))
@@ -756,6 +754,7 @@ class IPAddressMergeTestCase(ModelViewTestCase):
756
754
  self.add_permissions("ipam.change_ipaddress")
757
755
  num_ips_before = IPAddress.objects.all().count()
758
756
  ips = IPAddress.objects.all().exclude(pk__in=[self.dup_ip_1.pk, self.dup_ip_2.pk, self.dup_ip_3.pk])
757
+ self.assertGreaterEqual(len(ips), 6)
759
758
  ip_ct = ContentType.objects.get_for_model(IPAddress)
760
759
  locations = Location.objects.all()
761
760
  location_ct = ContentType.objects.get_for_model(Location)
nautobot/ipam/ui.py CHANGED
@@ -10,29 +10,12 @@ from nautobot.core.ui.object_detail import (
10
10
  Button,
11
11
  KeyValueTablePanel,
12
12
  ObjectFieldsPanel,
13
- ObjectsTablePanel,
14
13
  )
15
14
  from nautobot.core.views.utils import get_obj_from_context
16
15
 
17
16
  logger = logging.getLogger(__name__)
18
17
 
19
18
 
20
- # TODO: can be removed as a part of NAUTOBOT-1051
21
- class PrefixChildTablePanel(ObjectsTablePanel):
22
- def should_render(self, context: Context):
23
- if not super().should_render(context):
24
- return False
25
- return context.get("active_tab") == "prefixes"
26
-
27
-
28
- # TODO: can be removed as a part of NAUTOBOT-1051
29
- class IPAddressTablePanel(ObjectsTablePanel):
30
- def should_render(self, context: Context):
31
- if not super().should_render(context):
32
- return False
33
- return context.get("active_tab") == "ip-addresses"
34
-
35
-
36
19
  class AddChildPrefixButton(Button):
37
20
  """Custom button to add a child prefix inside a Prefix detail view."""
38
21
 
@@ -146,7 +146,9 @@ def process_vrfs(apps):
146
146
  Namespace = apps.get_model("ipam", "Namespace")
147
147
  VRF = apps.get_model("ipam", "VRF")
148
148
 
149
- global_ns = Namespace.objects.get(name=GLOBAL_NS)
149
+ global_ns, _ = Namespace.objects.get_or_create(
150
+ name=GLOBAL_NS, defaults={"description": "Default Global namespace. Created by Nautobot."}
151
+ )
150
152
  vrfs = VRF.objects.all().order_by("name", "rd")
151
153
  unique_non_empty_vrfs = vrfs.filter(enforce_unique=True).exclude(ip_addresses__isnull=True, prefixes__isnull=True)
152
154
  # At the point in the migration where we iterate through vrfs in global_ns_vrfs, every vrf that
@@ -257,6 +259,11 @@ def process_ip_addresses(apps):
257
259
  broadcast = new_parent_cidr[-1]
258
260
  # This can result in duplicate Prefixes being created in the global_ns but that will be
259
261
  # cleaned up subsequently in `process_prefix_duplicates`.
262
+ if orphaned_ip.vrf and orphaned_ip.vrf.namespace:
263
+ prefix_namespace = orphaned_ip.vrf.namespace
264
+ else:
265
+ prefix_namespace = global_ns
266
+
260
267
  new_parent = Prefix.objects.create(
261
268
  ip_version=orphaned_ip.ip_version,
262
269
  network=network,
@@ -264,7 +271,7 @@ def process_ip_addresses(apps):
264
271
  tenant=orphaned_ip.tenant,
265
272
  vrf=orphaned_ip.vrf,
266
273
  prefix_length=prefix_length,
267
- namespace=orphaned_ip.vrf.namespace if orphaned_ip.vrf else global_ns,
274
+ namespace=prefix_namespace,
268
275
  description=DESCRIPTION,
269
276
  )
270
277
  orphaned_ip.parent = new_parent
@@ -296,6 +303,13 @@ def process_prefix_duplicates(apps):
296
303
  Prefix = apps.get_model("ipam", "Prefix")
297
304
  global_namespace = Namespace.objects.get(name=GLOBAL_NS)
298
305
 
306
+ # First, assign any remaining prefixes with null namespace to Global namespace
307
+ null_namespace_prefixes = Prefix.objects.filter(namespace__isnull=True)
308
+ if null_namespace_prefixes.exists():
309
+ if "test" not in sys.argv:
310
+ print(f">>> Assigning {null_namespace_prefixes.count()} prefixes with null namespace to Global namespace")
311
+ null_namespace_prefixes.update(namespace=global_namespace)
312
+
299
313
  namespaces = list(Namespace.objects.all())
300
314
  # Always start with the Global Namespace.
301
315
  namespaces.remove(global_namespace)
@@ -5,8 +5,6 @@ import random
5
5
  from django.apps import apps
6
6
  from netaddr import IPNetwork
7
7
 
8
- from nautobot.ipam.models import get_default_namespace_pk
9
-
10
8
  # Calculate the probabilities to use for the maybe_subdivide() function defined below.
11
9
 
12
10
  # Frequency of IPv4 (leaf, network) Prefixes by each given mask length in a "realistic" data set.
@@ -129,7 +127,15 @@ def create_prefixes_and_ips(initial_subnet: str, apps=apps, seed="Nautobot"): #
129
127
 
130
128
  all_tenants = list(Tenant.objects.all())
131
129
  if hasattr(VRF, "namespace"):
132
- all_vrfs = list(VRF.objects.filter(namespace_id=get_default_namespace_pk()))
130
+ # Simulate mid-migration state: create Global namespace and assign all VRFs to it
131
+ # This creates more realistic test conditions for namespace-aware migration logic
132
+ Namespace = apps.get_model("ipam", "Namespace")
133
+ global_ns, _ = Namespace.objects.get_or_create(
134
+ name="Global", defaults={"description": "Default Global namespace. Created by Nautobot."}
135
+ )
136
+ # Pre-assign all VRFs to Global namespace to simulate early migration state
137
+ VRF.objects.all().update(namespace=global_ns)
138
+ all_vrfs = list(VRF.objects.filter(namespace_id=global_ns.pk))
133
139
  else:
134
140
  all_vrfs = list(VRF.objects.all())
135
141
 
nautobot/ipam/views.py CHANGED
@@ -44,6 +44,7 @@ from nautobot.extras.models import Role, SavedView, Status, Tag
44
44
  from nautobot.ipam.api import serializers
45
45
  from nautobot.tenancy.models import Tenant
46
46
  from nautobot.virtualization.models import VirtualMachine, VMInterface
47
+ from nautobot.vpn.tables import VPNTunnelEndpointTable
47
48
 
48
49
  from . import filters, forms, tables, ui
49
50
  from .models import (
@@ -241,6 +242,8 @@ class VRFUIViewSet(NautobotUIViewSet):
241
242
  table_class=tables.VRFDeviceAssignmentTable,
242
243
  table_filter="vrf",
243
244
  table_title="Assigned Devices",
245
+ related_list_url_name="dcim:device_list",
246
+ related_field_name="vrfs",
244
247
  exclude_columns=["vrf", "namespace", "rd"],
245
248
  add_button_route=None,
246
249
  ),
@@ -350,7 +353,7 @@ class PrefixUIViewSet(NautobotUIViewSet):
350
353
  ModelBreadcrumbItem(
351
354
  model=Prefix,
352
355
  reverse_query_params=lambda context: {"namespace": context["object"].namespace.pk},
353
- label_key="verbose_name_plural",
356
+ label_type="plural",
354
357
  ),
355
358
  ]
356
359
  }
@@ -395,7 +398,7 @@ class PrefixUIViewSet(NautobotUIViewSet):
395
398
  table_attribute="default_ancestors",
396
399
  table_title="Parent Prefixes",
397
400
  exclude_columns=["namespace"],
398
- related_field_name="prefixes",
401
+ related_field_name="ancestors",
399
402
  add_button_route=None,
400
403
  paginate=False,
401
404
  show_table_config_button=False,
@@ -406,7 +409,8 @@ class PrefixUIViewSet(NautobotUIViewSet):
406
409
  table_class=tables.VRFPrefixAssignmentTable,
407
410
  table_attribute="vrf_assignments",
408
411
  table_title="Assigned VRFs",
409
- related_field_name="vrfs",
412
+ related_field_name="prefix",
413
+ related_list_url_name="ipam:vrf_list",
410
414
  add_button_route=None,
411
415
  paginate=False,
412
416
  show_table_config_button=False,
@@ -418,7 +422,26 @@ class PrefixUIViewSet(NautobotUIViewSet):
418
422
  table_attribute="cloud_networks",
419
423
  table_title="Assigned Cloud Networks",
420
424
  exclude_columns=["actions", "assigned_prefix_count", "circuit_count", "cloud_service_count"],
421
- related_field_name="cloud_networks",
425
+ related_field_name="prefixes",
426
+ add_button_route=None,
427
+ paginate=False,
428
+ show_table_config_button=False,
429
+ ),
430
+ object_detail.ObjectsTablePanel(
431
+ section=SectionChoices.RIGHT_HALF,
432
+ weight=300,
433
+ table_class=VPNTunnelEndpointTable,
434
+ table_attribute="vpn_tunnel_endpoints",
435
+ table_title="VPN Tunnel Endpoints Protecting Prefix",
436
+ exclude_columns=[
437
+ "vpn_profile",
438
+ "destination_ipaddress",
439
+ "destination_fqdn",
440
+ "protected_prefixes_dg_count",
441
+ "protected_prefixes_count",
442
+ "actions",
443
+ ],
444
+ related_field_name="protected_prefixes",
422
445
  add_button_route=None,
423
446
  paginate=False,
424
447
  show_table_config_button=False,
@@ -432,13 +455,13 @@ class PrefixUIViewSet(NautobotUIViewSet):
432
455
  related_object_attribute="default_descendants",
433
456
  url_name="ipam:prefix_prefixes",
434
457
  panels=(
435
- ui.PrefixChildTablePanel(
458
+ object_detail.ObjectsTablePanel(
436
459
  section=SectionChoices.FULL_WIDTH,
437
460
  weight=100,
438
461
  context_table_key="prefix_table",
439
462
  add_button_route=None,
440
463
  include_paginator=True,
441
- header_extra_content_template_path="ipam/inc/prefix_header_extra_content_table.html",
464
+ related_field_name="within",
442
465
  ),
443
466
  ),
444
467
  ),
@@ -449,18 +472,24 @@ class PrefixUIViewSet(NautobotUIViewSet):
449
472
  related_object_attribute="all_ips",
450
473
  url_name="ipam:prefix_ipaddresses",
451
474
  panels=[
452
- ui.IPAddressTablePanel(
475
+ object_detail.ObjectsTablePanel(
453
476
  section=SectionChoices.FULL_WIDTH,
454
477
  weight=100,
455
478
  context_table_key="ip_table",
456
479
  add_button_route=None,
457
480
  include_paginator=True,
458
- header_extra_content_template_path="ipam/inc/prefix_header_extra_content_table.html",
481
+ related_field_name="prefix",
459
482
  ),
460
483
  ],
461
484
  ),
462
485
  ],
463
486
  extra_buttons=[
487
+ object_detail.Button(
488
+ weight=100,
489
+ label="Available",
490
+ render_on_tab_id="prefixes",
491
+ template_path="ipam/inc/toggle_available.html",
492
+ ),
464
493
  ui.AddChildPrefixButton(
465
494
  weight=200,
466
495
  label="Add Child Prefix",
@@ -470,8 +499,14 @@ class PrefixUIViewSet(NautobotUIViewSet):
470
499
  required_permissions=["ipam.add_prefix"],
471
500
  render_on_tab_id="prefixes",
472
501
  ),
502
+ object_detail.Button(
503
+ weight=100,
504
+ label="Available",
505
+ render_on_tab_id="ip-addresses",
506
+ template_path="ipam/inc/toggle_available.html",
507
+ ),
473
508
  ui.AddIPAddressButton(
474
- weight=300,
509
+ weight=200,
475
510
  label="Add an IP Address",
476
511
  link_name="ipam:ipaddress_add",
477
512
  color=ButtonActionColorChoices.SUBMIT,
@@ -503,7 +538,6 @@ class PrefixUIViewSet(NautobotUIViewSet):
503
538
  detail=True,
504
539
  url_path="prefixes",
505
540
  custom_view_base_action="view",
506
- custom_view_additional_permissions=["ipam.view_prefix"],
507
541
  )
508
542
  def prefixes(self, request, *args, **kwargs):
509
543
  instance = self.get_object()
@@ -516,7 +550,9 @@ class PrefixUIViewSet(NautobotUIViewSet):
516
550
 
517
551
  prefix_table = tables.PrefixDetailTable(
518
552
  child_prefixes,
553
+ configurable=True,
519
554
  exclude=["namespace"],
555
+ user=request.user,
520
556
  data_transform_callback=data_transform_callback,
521
557
  )
522
558
  if request.user.has_perm("ipam.change_prefix") or request.user.has_perm("ipam.delete_prefix"):
@@ -547,6 +583,7 @@ class PrefixUIViewSet(NautobotUIViewSet):
547
583
  "active_tab": "prefixes",
548
584
  "view_action": "prefixes",
549
585
  "show_available": request.GET.get("show_available", "true") == "true",
586
+ "badge_count_override": child_prefixes.count(),
550
587
  }
551
588
  )
552
589
 
@@ -568,7 +605,11 @@ class PrefixUIViewSet(NautobotUIViewSet):
568
605
  )
569
606
 
570
607
  ip_table = tables.IPAddressTable(
571
- ipaddresses, exclude=["parent__namespace"], data_transform_callback=data_transform_callback
608
+ ipaddresses,
609
+ configurable=True,
610
+ exclude=["parent__namespace"],
611
+ user=request.user,
612
+ data_transform_callback=data_transform_callback,
572
613
  )
573
614
  if request.user.has_perm("ipam.change_ipaddress") or request.user.has_perm("ipam.delete_ipaddress"):
574
615
  ip_table.columns.show("pk")
@@ -597,6 +638,7 @@ class PrefixUIViewSet(NautobotUIViewSet):
597
638
  "active_tab": "ip-addresses",
598
639
  "view_action": "ip_addresses",
599
640
  "show_available": request.GET.get("show_available", "true") == "true",
641
+ "badge_count_override": ipaddresses.count(),
600
642
  }
601
643
  )
602
644
 
File without changes
@@ -0,0 +1 @@
1
+ """REST API module for load_balancers app."""
@@ -0,0 +1,75 @@
1
+ """API serializers for nautobot_load_balancer_models."""
2
+
3
+ from nautobot.core.api import NautobotModelSerializer, ValidatedModelSerializer
4
+ from nautobot.extras.api.mixins import TaggedModelSerializerMixin
5
+ from nautobot.load_balancers import models
6
+
7
+
8
+ class VirtualServerSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
9
+ """VirtualServer Serializer."""
10
+
11
+ class Meta:
12
+ """Meta attributes."""
13
+
14
+ model = models.VirtualServer
15
+ fields = "__all__"
16
+
17
+
18
+ class LoadBalancerPoolSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
19
+ """LoadBalancerPool Serializer."""
20
+
21
+ class Meta:
22
+ """Meta attributes."""
23
+
24
+ model = models.LoadBalancerPool
25
+ fields = "__all__"
26
+
27
+
28
+ class LoadBalancerPoolMemberSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
29
+ """LoadBalancerPoolMember Serializer."""
30
+
31
+ class Meta:
32
+ """Meta attributes."""
33
+
34
+ model = models.LoadBalancerPoolMember
35
+ fields = "__all__"
36
+
37
+
38
+ class HealthCheckMonitorSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
39
+ """HealthCheckMonitor Serializer."""
40
+
41
+ class Meta:
42
+ """Meta attributes."""
43
+
44
+ model = models.HealthCheckMonitor
45
+ fields = "__all__"
46
+
47
+
48
+ class CertificateProfileSerializer(TaggedModelSerializerMixin, NautobotModelSerializer): # pylint: disable=too-many-ancestors
49
+ """CertificateProfile Serializer."""
50
+
51
+ class Meta:
52
+ """Meta attributes."""
53
+
54
+ model = models.CertificateProfile
55
+ fields = "__all__"
56
+
57
+
58
+ class VirtualServerCertificateProfileAssignmentSerializer(ValidatedModelSerializer): # pylint: disable=too-many-ancestors
59
+ """VirtualServerCertificateProfileAssignment Serializer."""
60
+
61
+ class Meta:
62
+ """Meta attributes."""
63
+
64
+ model = models.VirtualServerCertificateProfileAssignment
65
+ fields = "__all__"
66
+
67
+
68
+ class LoadBalancerPoolMemberCertificateProfileAssignmentSerializer(ValidatedModelSerializer): # pylint: disable=too-many-ancestors
69
+ """LoadBalancerPoolMemberCertificateProfileAssignment Serializer."""
70
+
71
+ class Meta:
72
+ """Meta attributes."""
73
+
74
+ model = models.LoadBalancerPoolMemberCertificateProfileAssignment
75
+ fields = "__all__"