nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (420) hide show
  1. nautobot/apps/choices.py +0 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +9 -1
  5. nautobot/circuits/filters.py +3 -2
  6. nautobot/circuits/navigation.py +3 -2
  7. nautobot/circuits/templates/circuits/circuit.html +1 -1
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination.html +1 -1
  10. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  11. nautobot/circuits/templates/circuits/circuittype.html +1 -1
  12. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  13. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  14. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  15. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  16. nautobot/cloud/filters.py +1 -1
  17. nautobot/cloud/navigation.py +3 -2
  18. nautobot/core/api/schema.py +1 -1
  19. nautobot/core/api/serializers.py +6 -1
  20. nautobot/core/api/urls.py +1 -0
  21. nautobot/core/api/views.py +8 -0
  22. nautobot/core/apps/__init__.py +11 -10
  23. nautobot/core/celery/__init__.py +3 -5
  24. nautobot/core/checks.py +46 -0
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
  26. nautobot/core/cli/migrate_deprecated_templates.py +200 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/forms/forms.py +1 -1
  30. nautobot/core/jobs/__init__.py +6 -3
  31. nautobot/core/jobs/groups.py +31 -1
  32. nautobot/core/management/commands/generate_test_data.py +28 -9
  33. nautobot/core/models/generics.py +9 -1
  34. nautobot/core/models/tree_queries.py +10 -5
  35. nautobot/core/settings.py +18 -12
  36. nautobot/core/settings.yaml +13 -7
  37. nautobot/core/signals.py +12 -1
  38. nautobot/core/tables.py +13 -6
  39. nautobot/core/templates/40x.html +1 -1
  40. nautobot/core/templates/500.html +2 -2
  41. nautobot/core/templates/admin/config/config.html +12 -12
  42. nautobot/core/templates/admin/index.html +3 -3
  43. nautobot/core/templates/buttons/export.html +1 -1
  44. nautobot/core/templates/components/button/dropdown.html +5 -3
  45. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  46. nautobot/core/templates/components/panel/panel.html +3 -3
  47. nautobot/core/templates/components/tab/content_wrapper.html +2 -3
  48. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  49. nautobot/core/templates/echarts/echarts.html +1 -1
  50. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  51. nautobot/core/templates/generic/object_bulk_create.html +4 -3
  52. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  53. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  54. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  55. nautobot/core/templates/generic/object_create.html +5 -4
  56. nautobot/core/templates/generic/object_import.html +2 -1
  57. nautobot/core/templates/generic/object_list.html +12 -4
  58. nautobot/core/templates/generic/object_notes.html +5 -3
  59. nautobot/core/templates/generic/object_retrieve.html +2 -3
  60. nautobot/core/templates/graphene/graphiql.html +7 -7
  61. nautobot/core/templates/home.html +1 -1
  62. nautobot/core/templates/import_success.html +2 -1
  63. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  64. nautobot/core/templates/inc/created_updated.html +7 -3
  65. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  66. nautobot/core/templates/inc/form_static_field.html +6 -0
  67. nautobot/core/templates/inc/header.html +1 -1
  68. nautobot/core/templates/inc/image_attachments.html +2 -1
  69. nautobot/core/templates/inc/nav_menu.html +2 -1
  70. nautobot/core/templates/inc/search_panel.html +4 -4
  71. nautobot/core/templates/login.html +4 -2
  72. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  73. nautobot/core/templates/redoc_ui.html +7 -0
  74. nautobot/core/templates/search.html +1 -1
  75. nautobot/core/templates/swagger_ui.html +17 -3
  76. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  77. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  78. nautobot/core/templates/utilities/obj_table.html +10 -2
  79. nautobot/core/templates/utilities/render_field.html +7 -7
  80. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  81. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +4 -4
  82. nautobot/core/templates/utilities/theme_preview.html +16 -3
  83. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  84. nautobot/core/templatetags/helpers.py +52 -6
  85. nautobot/core/testing/api.py +68 -9
  86. nautobot/core/testing/filters.py +0 -23
  87. nautobot/core/testing/integration.py +23 -10
  88. nautobot/core/testing/mixins.py +2 -0
  89. nautobot/core/testing/views.py +4 -0
  90. nautobot/core/tests/integration/test_app_home.py +34 -30
  91. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  92. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  93. nautobot/core/tests/runner.py +9 -1
  94. nautobot/core/tests/test_api.py +5 -3
  95. nautobot/core/tests/test_breadcrumbs.py +6 -7
  96. nautobot/core/tests/test_checks.py +28 -0
  97. nautobot/core/tests/test_cli.py +40 -0
  98. nautobot/core/tests/test_config.py +2 -1
  99. nautobot/core/tests/test_forms.py +55 -13
  100. nautobot/core/tests/test_jobs.py +75 -1
  101. nautobot/core/tests/test_nautobot_server.py +2 -0
  102. nautobot/core/tests/test_navigations.py +76 -1
  103. nautobot/core/tests/test_patch_social_django.py +42 -0
  104. nautobot/core/tests/test_tables.py +3 -1
  105. nautobot/core/tests/test_templatetags_helpers.py +53 -13
  106. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  107. nautobot/core/tests/test_tree_queries.py +14 -1
  108. nautobot/core/tests/test_ui.py +1 -1
  109. nautobot/core/tests/test_utils.py +31 -4
  110. nautobot/core/tests/test_views.py +159 -31
  111. nautobot/core/ui/breadcrumbs.py +2 -12
  112. nautobot/core/ui/choices.py +142 -10
  113. nautobot/core/ui/constants.py +76 -12
  114. nautobot/core/ui/object_detail.py +92 -12
  115. nautobot/core/urls.py +12 -1
  116. nautobot/core/utils/cache.py +2 -1
  117. nautobot/core/utils/filtering.py +17 -17
  118. nautobot/core/utils/lookup.py +3 -8
  119. nautobot/core/utils/module_loading.py +21 -0
  120. nautobot/core/utils/patch_social_django.py +128 -0
  121. nautobot/core/views/__init__.py +38 -1
  122. nautobot/core/views/generic.py +3 -3
  123. nautobot/core/views/mixins.py +15 -3
  124. nautobot/core/views/renderers.py +2 -0
  125. nautobot/core/views/viewsets.py +2 -1
  126. nautobot/data_validation/apps.py +1 -5
  127. nautobot/data_validation/custom_validators.py +4 -4
  128. nautobot/data_validation/filters.py +1 -1
  129. nautobot/data_validation/forms.py +40 -0
  130. nautobot/data_validation/migrations/0001_initial.py +0 -7
  131. nautobot/data_validation/migrations/0002_data_migration_from_app.py +0 -12
  132. nautobot/data_validation/models.py +16 -7
  133. nautobot/data_validation/navigation.py +8 -1
  134. nautobot/data_validation/tables.py +12 -5
  135. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  136. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  137. nautobot/data_validation/tests/__init__.py +2 -2
  138. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  139. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  140. nautobot/data_validation/tests/test_filters.py +8 -6
  141. nautobot/data_validation/tests/test_models.py +15 -0
  142. nautobot/data_validation/tests/test_views.py +190 -32
  143. nautobot/data_validation/urls.py +2 -5
  144. nautobot/data_validation/views.py +73 -40
  145. nautobot/dcim/api/serializers.py +0 -13
  146. nautobot/dcim/apps.py +4 -0
  147. nautobot/dcim/choices.py +16 -0
  148. nautobot/dcim/custom_validators.py +84 -0
  149. nautobot/dcim/filter_mixins.py +353 -4
  150. nautobot/dcim/{filters/__init__.py → filters.py} +2 -35
  151. nautobot/dcim/forms.py +1 -1
  152. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  153. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  154. nautobot/dcim/models/device_components.py +81 -68
  155. nautobot/dcim/models/devices.py +13 -16
  156. nautobot/dcim/navigation.py +7 -6
  157. nautobot/dcim/tables/devices.py +3 -0
  158. nautobot/dcim/tables/template_code.py +14 -14
  159. nautobot/dcim/templates/dcim/cable.html +2 -61
  160. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  161. nautobot/dcim/templates/dcim/cable_edit.html +2 -5
  162. nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
  163. nautobot/dcim/templates/dcim/cable_trace.html +1 -3
  164. nautobot/dcim/templates/dcim/cable_update.html +5 -0
  165. nautobot/dcim/templates/dcim/consoleport.html +6 -5
  166. nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
  167. nautobot/dcim/templates/dcim/device/config.html +2 -2
  168. nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
  169. nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
  170. nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
  171. nautobot/dcim/templates/dcim/device/frontports.html +1 -1
  172. nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
  173. nautobot/dcim/templates/dcim/device/inventory.html +1 -1
  174. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  175. nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
  176. nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
  177. nautobot/dcim/templates/dcim/device/powerports.html +1 -1
  178. nautobot/dcim/templates/dcim/device/rearports.html +1 -1
  179. nautobot/dcim/templates/dcim/device/status.html +8 -8
  180. nautobot/dcim/templates/dcim/device/wireless.html +1 -1
  181. nautobot/dcim/templates/dcim/device.html +1 -1
  182. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  183. nautobot/dcim/templates/dcim/device_create.html +5 -3
  184. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  185. nautobot/dcim/templates/dcim/device_list.html +73 -10
  186. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  187. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  188. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  189. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  190. nautobot/dcim/templates/dcim/frontport.html +9 -8
  191. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  192. nautobot/dcim/templates/dcim/interface.html +26 -6
  193. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  194. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  195. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  196. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  197. nautobot/dcim/templates/dcim/location_retrieve.html +1 -242
  198. nautobot/dcim/templates/dcim/module/base.html +49 -9
  199. nautobot/dcim/templates/dcim/module_list.html +57 -8
  200. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  201. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  202. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  203. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  204. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  205. nautobot/dcim/templates/dcim/powerport.html +5 -4
  206. nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
  207. nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
  208. nautobot/dcim/templates/dcim/rearport.html +7 -6
  209. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  210. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  211. nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
  212. nautobot/dcim/tests/integration/test_controller.py +1 -0
  213. nautobot/dcim/tests/test_api.py +8 -0
  214. nautobot/dcim/tests/test_custom_validators.py +229 -0
  215. nautobot/dcim/tests/test_filters.py +12 -6
  216. nautobot/dcim/tests/test_models.py +63 -4
  217. nautobot/dcim/tests/test_views.py +63 -22
  218. nautobot/dcim/urls.py +64 -21
  219. nautobot/dcim/utils.py +3 -3
  220. nautobot/dcim/views.py +547 -273
  221. nautobot/extras/api/views.py +9 -1
  222. nautobot/extras/choices.py +2 -13
  223. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  224. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  225. nautobot/extras/{filters/__init__.py → filters.py} +14 -46
  226. nautobot/extras/forms/forms.py +5 -13
  227. nautobot/extras/forms/mixins.py +0 -41
  228. nautobot/extras/management/__init__.py +9 -0
  229. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  230. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  231. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  232. nautobot/extras/models/__init__.py +1 -2
  233. nautobot/extras/models/approvals.py +22 -13
  234. nautobot/extras/models/contacts.py +2 -0
  235. nautobot/extras/models/groups.py +44 -5
  236. nautobot/extras/models/jobs.py +59 -1
  237. nautobot/extras/models/mixins.py +28 -0
  238. nautobot/extras/models/models.py +13 -0
  239. nautobot/extras/models/secrets.py +1 -0
  240. nautobot/extras/models/statuses.py +0 -15
  241. nautobot/extras/navigation.py +13 -9
  242. nautobot/extras/plugins/__init__.py +33 -55
  243. nautobot/extras/plugins/tables.py +3 -3
  244. nautobot/extras/plugins/urls.py +2 -21
  245. nautobot/extras/plugins/utils.py +1 -33
  246. nautobot/extras/plugins/views.py +0 -4
  247. nautobot/extras/signals.py +20 -19
  248. nautobot/extras/tables.py +52 -68
  249. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  250. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  251. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  252. nautobot/extras/templates/extras/computedfield.html +1 -1
  253. nautobot/extras/templates/extras/configcontext.html +1 -1
  254. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  255. nautobot/extras/templates/extras/customfield.html +1 -1
  256. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  257. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  258. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  259. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -96
  260. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  261. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  262. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  263. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  264. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  265. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  266. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  267. nautobot/extras/templates/extras/job.html +29 -12
  268. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  269. nautobot/extras/templates/extras/job_edit.html +52 -46
  270. nautobot/extras/templates/extras/job_list.html +29 -25
  271. nautobot/extras/templates/extras/marketplace.html +5 -9
  272. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  273. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  274. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -37
  275. nautobot/extras/templates/extras/plugin_detail.html +26 -21
  276. nautobot/extras/templates/extras/plugins_list.html +16 -26
  277. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  278. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  279. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  280. nautobot/extras/templates/extras/tag.html +1 -1
  281. nautobot/extras/templatetags/custom_links.py +12 -12
  282. nautobot/extras/templatetags/job_buttons.py +14 -12
  283. nautobot/extras/test_jobs/invalid_import.py +9 -0
  284. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  285. nautobot/extras/test_jobs/missing_import.py +11 -0
  286. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  287. nautobot/extras/tests/integration/test_customfields.py +8 -7
  288. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
  289. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  290. nautobot/extras/tests/integration/test_plugins.py +18 -6
  291. nautobot/extras/tests/test_api.py +27 -18
  292. nautobot/extras/tests/test_approvals.py +38 -38
  293. nautobot/extras/tests/test_changelog.py +35 -3
  294. nautobot/extras/tests/test_customfields.py +22 -13
  295. nautobot/extras/tests/test_customfields_filters.py +479 -0
  296. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  297. nautobot/extras/tests/test_filters.py +21 -19
  298. nautobot/extras/tests/test_forms.py +18 -21
  299. nautobot/extras/tests/test_jobs.py +25 -4
  300. nautobot/extras/tests/test_migrations.py +1 -0
  301. nautobot/extras/tests/test_models.py +13 -31
  302. nautobot/extras/tests/test_plugins.py +36 -10
  303. nautobot/extras/tests/test_views.py +31 -30
  304. nautobot/extras/views.py +81 -19
  305. nautobot/ipam/factory.py +7 -0
  306. nautobot/ipam/filter_mixins.py +38 -0
  307. nautobot/ipam/filters.py +27 -38
  308. nautobot/ipam/formfields.py +1 -1
  309. nautobot/ipam/forms.py +6 -3
  310. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  311. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  312. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  313. nautobot/ipam/models.py +29 -2
  314. nautobot/ipam/navigation.py +3 -2
  315. nautobot/ipam/signals.py +71 -0
  316. nautobot/ipam/tables.py +13 -6
  317. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  318. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  319. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  320. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  321. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  322. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  323. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  324. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  325. nautobot/ipam/templates/ipam/service.html +1 -1
  326. nautobot/ipam/templates/ipam/vlan.html +1 -1
  327. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  328. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  329. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  330. nautobot/ipam/tests/test_api.py +13 -6
  331. nautobot/ipam/tests/test_filters.py +10 -0
  332. nautobot/ipam/tests/test_forms.py +1 -1
  333. nautobot/ipam/tests/test_models.py +43 -1
  334. nautobot/ipam/tests/test_tables.py +1 -2
  335. nautobot/ipam/tests/test_utils.py +1 -1
  336. nautobot/ipam/tests/test_views.py +13 -14
  337. nautobot/ipam/ui.py +0 -17
  338. nautobot/ipam/utils/migrations.py +16 -2
  339. nautobot/ipam/utils/testing.py +9 -3
  340. nautobot/ipam/views.py +46 -6
  341. nautobot/project-static/dist/css/nautobot.css +1 -1
  342. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  343. nautobot/project-static/dist/js/nautobot.js +1 -1
  344. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  345. nautobot/project-static/js/cabletrace.js +1 -1
  346. nautobot/project-static/js/interface_filtering.js +20 -16
  347. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  348. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  349. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  350. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  351. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  352. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  353. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  354. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  355. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  356. nautobot/project-static/nautobot-icons/security.svg +3 -0
  357. nautobot/project-static/nautobot-icons/server.svg +1 -1
  358. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  359. nautobot/project-static/nautobot-icons/star.svg +1 -1
  360. nautobot/tenancy/api/serializers.py +1 -0
  361. nautobot/tenancy/api/views.py +2 -1
  362. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  363. nautobot/tenancy/navigation.py +3 -1
  364. nautobot/tenancy/tests/test_filters.py +0 -2
  365. nautobot/tenancy/views.py +2 -1
  366. nautobot/ui/src/js/collapse.js +3 -3
  367. nautobot/ui/src/js/nautobot.js +16 -0
  368. nautobot/ui/src/scss/colors.scss +1 -1
  369. nautobot/ui/src/scss/nautobot.scss +61 -28
  370. nautobot/users/templates/users/profile.html +45 -12
  371. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  372. nautobot/users/tests/test_api.py +4 -0
  373. nautobot/users/views.py +4 -2
  374. nautobot/virtualization/models.py +1 -68
  375. nautobot/virtualization/navigation.py +3 -2
  376. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  377. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  378. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  379. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  380. nautobot/virtualization/tests/test_api.py +3 -0
  381. nautobot/virtualization/tests/test_models.py +44 -4
  382. nautobot/vpn/__init__.py +0 -0
  383. nautobot/vpn/api/serializers.py +113 -0
  384. nautobot/vpn/api/urls.py +19 -0
  385. nautobot/vpn/api/views.py +70 -0
  386. nautobot/vpn/apps.py +8 -0
  387. nautobot/vpn/choices.py +171 -0
  388. nautobot/vpn/factory.py +209 -0
  389. nautobot/vpn/filters.py +233 -0
  390. nautobot/vpn/forms.py +486 -0
  391. nautobot/vpn/homepage.py +19 -0
  392. nautobot/vpn/migrations/0001_initial.py +541 -0
  393. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  394. nautobot/vpn/migrations/__init__.py +0 -0
  395. nautobot/vpn/models.py +527 -0
  396. nautobot/vpn/navigation.py +98 -0
  397. nautobot/vpn/tables.py +380 -0
  398. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  399. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  400. nautobot/vpn/tests/__init__.py +0 -0
  401. nautobot/vpn/tests/test_api.py +341 -0
  402. nautobot/vpn/tests/test_filters.py +139 -0
  403. nautobot/vpn/tests/test_forms.py +294 -0
  404. nautobot/vpn/tests/test_models.py +97 -0
  405. nautobot/vpn/tests/test_views.py +281 -0
  406. nautobot/vpn/urls.py +16 -0
  407. nautobot/vpn/views.py +437 -0
  408. nautobot/wireless/navigation.py +3 -2
  409. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  410. nautobot/wireless/tests/test_api.py +1 -1
  411. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +14 -14
  412. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
  413. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  414. nautobot/data_validation/template_content.py +0 -42
  415. nautobot/dcim/filters/mixins.py +0 -354
  416. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  417. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  418. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  419. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  420. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
@@ -5,7 +5,7 @@
5
5
  * ---
6
6
  * Critical difference between this and default Bootstrap 5 collapse implementation is that Bootstrap only supports
7
7
  * toggling individual panel states without an option to toggle them collectively. See an explanation below:
8
- * Bootstrap 5 on "Collapse All" button click: | Nautobot on "Collapse All" button click:
8
+ * Bootstrap 5 on "Collapse All Groups" button click: | Nautobot on "Collapse All Groups" button click:
9
9
  * * X [expanded] -> [collapsed] | * X [expanded] -> [collapsed]
10
10
  * * Y [collapsed] -> [expanded] | * Y [collapsed] -> [collapsed]
11
11
  * * Z [expanded] -> [collapsed] | * Z [expanded] -> [collapsed]
@@ -45,7 +45,7 @@ export const initializeCollapseToggleAll = () => {
45
45
  .filter((collapseToggleAll) => areAll(getCollapseToggleAllTargets(collapseToggleAll), 'collapsed'))
46
46
  .forEach((collapseToggleAll) => {
47
47
  collapseToggleAll.setAttribute('aria-expanded', 'false');
48
- collapseToggleAll.textContent = 'Expand All';
48
+ collapseToggleAll.textContent = 'Expand All Groups';
49
49
  });
50
50
 
51
51
  const onShownBsCollapse = () =>
@@ -53,7 +53,7 @@ export const initializeCollapseToggleAll = () => {
53
53
  .filter((collapseToggleAll) => areAll(getCollapseToggleAllTargets(collapseToggleAll), 'expanded'))
54
54
  .forEach((collapseToggleAll) => {
55
55
  collapseToggleAll.setAttribute('aria-expanded', 'true');
56
- collapseToggleAll.textContent = 'Collapse All';
56
+ collapseToggleAll.textContent = 'Collapse All Groups';
57
57
  });
58
58
 
59
59
  // Using event delegation pattern here to avoid re-creating listeners each time DOM is modified.
@@ -109,4 +109,20 @@ document.addEventListener('DOMContentLoaded', () => {
109
109
  event.detail.path = isFavorite ? element.dataset.deleteUrl : element.dataset.addUrl;
110
110
  };
111
111
  window.setRequestUrl = setRequestUrl;
112
+
113
+ // Remove the blur after clicking the footer links that are opening mostly in the new tab
114
+ // Keeping focus on those items is what keeps the tooltip as well
115
+ document.querySelectorAll('a[data-bs-toggle="tooltip"], div[data-bs-toggle="tooltip"] > a').forEach((el) => {
116
+ el.addEventListener('click', () => {
117
+ el.blur();
118
+ });
119
+ });
120
+
121
+ // When modal is being closed, bootstrap automatically restore the focus on the element that was triggering the modal
122
+ // As a result, after modal close tooltip was triggered as well
123
+ document.addEventListener('hidden.bs.modal', () => {
124
+ document.querySelectorAll('[data-bs-toggle="tooltip"] > a').forEach((el) => {
125
+ el.blur();
126
+ });
127
+ });
112
128
  });
@@ -36,7 +36,7 @@ $gray-3-dark: #b5b5b5;
36
36
 
37
37
  $blue-0-dark: #339dff;
38
38
  $blue-1-dark: #1a2936;
39
- $blue-2-dark: #0e1C2B;
39
+ $blue-2-dark: #0e1c2b;
40
40
  $orange-0-dark: #ff9933;
41
41
  $orange-1-dark: #2a1c0f;
42
42
  $orange-2-dark: #3d2a16;
@@ -414,8 +414,8 @@ $btn-close-opacity: 0.68;
414
414
  $btn-close-hover-opacity: 1;
415
415
  $btn-close-focus-opacity: 1;
416
416
  $btn-close-disabled-opacity: 0.31;
417
- $btn-close-filter: invert(1%) sepia(10%) saturate(233%) hue-rotate(314deg) brightness(99%) contrast(81%);
418
- $btn-close-filter-dark: invert(96%) sepia(0%) saturate(1%) hue-rotate(250deg) brightness(86%) contrast(91%);
417
+ $btn-close-filter: invert(1%) sepia(10%) saturate(233%) hue-rotate(314deg) brightness(99%) contrast(81%); /* $black-0: #1a1a1a; */
418
+ $btn-close-filter-dark: invert(100%); /* $black-0-dark: #ffffff; */
419
419
  $btn-close-white-filter: $btn-close-filter-dark;
420
420
 
421
421
  /* Code */
@@ -431,6 +431,18 @@ $kbd-bg: var(--#{$prefix}secondary-bg);
431
431
  /* 4. Include any default map overrides here */
432
432
  $theme-colors-rgb-dark: map-loop($theme-colors-dark, to-rgb, "$value");
433
433
 
434
+ /* Breakpoints */
435
+ $sidenav-width-for-breakpoints: 240px; // this is only for calculations below; sidenav has separate variable in rem in Layout section
436
+
437
+ $grid-breakpoints: (
438
+ xs: 0,
439
+ sm: 576px + $sidenav-width-for-breakpoints,
440
+ md: 768px + $sidenav-width-for-breakpoints,
441
+ lg: 992px + $sidenav-width-for-breakpoints,
442
+ xl: 1200px + $sidenav-width-for-breakpoints,
443
+ xxl: 1400px + $sidenav-width-for-breakpoints
444
+ );
445
+
434
446
  /* 5. Include remainder of required parts */
435
447
  @import "bootstrap/scss/maps";
436
448
  @import "bootstrap/scss/mixins";
@@ -746,12 +758,6 @@ $nautobot-chevron-svg: "<svg viewBox='0 0 17 20' fill='none' xmlns='http://www.w
746
758
  }
747
759
  }
748
760
 
749
- @mixin nb-firefox-only {
750
- @supports (-moz-appearance: none) {
751
- @content;
752
- }
753
- }
754
-
755
761
  /* Links */
756
762
  a,
757
763
  button {
@@ -1114,6 +1120,11 @@ textarea {
1114
1120
  }
1115
1121
  }
1116
1122
 
1123
+ .nb-form-check-input-sm {
1124
+ height: 1rem; /* 16px */
1125
+ width: 1rem; /* 16px */
1126
+ }
1127
+
1117
1128
  .form-text {
1118
1129
  display: block;
1119
1130
  font-weight: $font-weight-normal;
@@ -1275,11 +1286,13 @@ textarea {
1275
1286
  &.bg-danger,
1276
1287
  &.bg-info,
1277
1288
  &.bg-primary,
1289
+ &.bg-secondary,
1278
1290
  &.bg-success,
1279
1291
  &.bg-warning,
1280
1292
  &.text-bg-danger,
1281
1293
  &.text-bg-info,
1282
1294
  &.text-bg-primary,
1295
+ &.text-bg-secondary,
1283
1296
  &.text-bg-success,
1284
1297
  &.text-bg-warning {
1285
1298
  --#{$prefix}badge-color: var(--#{$prefix}body-bg);
@@ -1413,6 +1426,14 @@ textarea {
1413
1426
  }
1414
1427
  }
1415
1428
 
1429
+ /* Code */
1430
+ pre:not(:has(code.hljs)) { /* Do not include highlight.js in this rule because it has its own set of styles. */
1431
+ background: var(--#{$prefix}secondary-bg);
1432
+ border-radius: $border-radius;
1433
+ padding-block: map.get($spacers, 8);
1434
+ padding-inline: map.get($spacers, 10);
1435
+ }
1436
+
1416
1437
  /* Collapse */
1417
1438
  .nb-collapse-toggle {
1418
1439
  @include transition($btn-transition);
@@ -1427,7 +1448,7 @@ textarea {
1427
1448
  display: inline-block; /* `transform` does not work on standard `inline` elements, like `span`, hence `inline-block`. */
1428
1449
  }
1429
1450
 
1430
- &:not([aria-expanded="true"]) > * {
1451
+ &:not([aria-expanded="true"]) > .mdi-chevron-down {
1431
1452
  transform: rotate(-90deg); /* Rotate chevron icon when collapse is closed. */
1432
1453
  }
1433
1454
  }
@@ -1452,7 +1473,8 @@ textarea {
1452
1473
  $nb-drawer-closed-width: 0;
1453
1474
  $nb-drawer-open-width: 20rem; /* 320px */
1454
1475
  $sidenav-width-collapsed: 4rem; /* 64px */
1455
- $sidenav-width-expanded: 15rem; /* 240px */
1476
+ $sidenav-width-expanded: ($sidenav-width-for-breakpoints / 16px) * 1rem; /* 16px is default browser conversion rate */
1477
+ /* we're converting to rem to have better web accessibility support */
1456
1478
 
1457
1479
  * {
1458
1480
  margin: 0;
@@ -1515,8 +1537,8 @@ body {
1515
1537
  /* Sidenav (a.k.a. navbar, nav menu, sidebar) */
1516
1538
  #sidenav {
1517
1539
  $sidenav-chevron-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill-rule='evenodd' clip-rule='evenodd' d='M14.53 5.46967C14.8229 5.76256 14.8229 6.23744 14.53 6.53033L10.7928 10.2676C10.388 10.6724 10.1167 10.9444 9.92461 11.1708C9.73817 11.3904 9.67071 11.5171 9.6393 11.6137C9.55773 11.8648 9.55773 12.1352 9.6393 12.3863C9.67071 12.4829 9.73817 12.6096 9.92461 12.8292C10.1167 13.0556 10.388 13.3276 10.7928 13.7324L14.53 17.4697C14.8229 17.7626 14.8229 18.2374 14.53 18.5303C14.2371 18.8232 13.7623 18.8232 13.4694 18.5303L9.73211 14.7931L9.71059 14.7715C9.33288 14.3939 9.01761 14.0786 8.7811 13.8C8.53443 13.5094 8.32969 13.2098 8.21272 12.8498C8.03326 12.2975 8.03326 11.7025 8.21272 11.1502C8.32969 10.7902 8.53443 10.4906 8.7811 10.2C9.01761 9.9214 9.33287 9.60615 9.71058 9.22845L9.73211 9.20693L13.4694 5.46967C13.7623 5.17678 14.2371 5.17678 14.53 5.46967Z' fill='#{$nav-white-0}'/></svg>";
1518
- $sidenav-favorite-icon-svg: "<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path d='M9.13784 4.72433C10.0591 2.8819 10.5197 1.96069 11.143 1.66508C11.6854 1.40782 12.3146 1.40782 12.8571 1.66508C13.4804 1.96069 13.941 2.88191 14.8622 4.72434L15.2215 5.4429C15.5059 6.01177 15.6481 6.29621 15.8562 6.51399C16.0403 6.70676 16.2612 6.86077 16.5058 6.96694C16.782 7.08689 17.0981 7.12201 17.7302 7.19225L18.0564 7.22849C20.2646 7.47385 21.3687 7.59653 21.8677 8.10561C22.3013 8.54802 22.5055 9.16639 22.4205 9.78001C22.3227 10.4861 21.5086 11.242 19.8805 12.7539L19.5361 13.0736C18.9804 13.5897 18.7025 13.8477 18.5419 14.1604C18.4 14.4368 18.3244 14.7424 18.3211 15.0531C18.3174 15.4046 18.4446 15.7671 18.699 16.4921V16.4921C19.5294 18.8587 19.9446 20.042 19.673 20.7338C19.4374 21.3339 18.927 21.7836 18.3021 21.9418C17.5816 22.1241 16.4767 21.5717 14.267 20.4668L13.4311 20.0489C12.9064 19.7865 12.644 19.6553 12.3688 19.6037C12.1251 19.558 11.8749 19.558 11.6312 19.6037C11.356 19.6553 11.0937 19.7865 10.5689 20.0489L9.73299 20.4668C7.52325 21.5717 6.41838 22.1241 5.69791 21.9418C5.07298 21.7836 4.56261 21.3339 4.32703 20.7338C4.05543 20.042 4.47062 18.8587 5.30101 16.4921V16.4921C5.55541 15.7671 5.68261 15.4046 5.67891 15.0531C5.67565 14.7424 5.60005 14.4368 5.45811 14.1604C5.29751 13.8477 5.01964 13.5897 4.46388 13.0736L4.11954 12.7539C2.49141 11.242 1.67734 10.4861 1.57953 9.78001C1.49453 9.16639 1.69867 8.54802 2.13229 8.10561C2.63127 7.59653 3.73538 7.47385 5.9436 7.22849L6.26976 7.19225C6.90189 7.12201 7.21795 7.08689 7.49423 6.96694C7.73879 6.86077 7.95965 6.70676 8.14383 6.51399C8.35189 6.29621 8.49411 6.01178 8.77855 5.44291L9.13784 4.72433Z' fill='none' stroke='#{$nav-white-0}' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>";
1519
- $sidenav-favorite-icon-svg-filled: "<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path d='M9.13784 4.72433C10.0591 2.8819 10.5197 1.96069 11.143 1.66508C11.6854 1.40782 12.3146 1.40782 12.8571 1.66508C13.4804 1.96069 13.941 2.88191 14.8622 4.72434L15.2215 5.4429C15.5059 6.01177 15.6481 6.29621 15.8562 6.51399C16.0403 6.70676 16.2612 6.86077 16.5058 6.96694C16.782 7.08689 17.0981 7.12201 17.7302 7.19225L18.0564 7.22849C20.2646 7.47385 21.3687 7.59653 21.8677 8.10561C22.3013 8.54802 22.5055 9.16639 22.4205 9.78001C22.3227 10.4861 21.5086 11.242 19.8805 12.7539L19.5361 13.0736C18.9804 13.5897 18.7025 13.8477 18.5419 14.1604C18.4 14.4368 18.3244 14.7424 18.3211 15.0531C18.3174 15.4046 18.4446 15.7671 18.699 16.4921V16.4921C19.5294 18.8587 19.9446 20.042 19.673 20.7338C19.4374 21.3339 18.927 21.7836 18.3021 21.9418C17.5816 22.1241 16.4767 21.5717 14.267 20.4668L13.4311 20.0489C12.9064 19.7865 12.644 19.6553 12.3688 19.6037C12.1251 19.558 11.8749 19.558 11.6312 19.6037C11.356 19.6553 11.0937 19.7865 10.5689 20.0489L9.73299 20.4668C7.52325 21.5717 6.41838 22.1241 5.69791 21.9418C5.07298 21.7836 4.56261 21.3339 4.32703 20.7338C4.05543 20.042 4.47062 18.8587 5.30101 16.4921V16.4921C5.55541 15.7671 5.68261 15.4046 5.67891 15.0531C5.67565 14.7424 5.60005 14.4368 5.45811 14.1604C5.29751 13.8477 5.01964 13.5897 4.46388 13.0736L4.11954 12.7539C2.49141 11.242 1.67734 10.4861 1.57953 9.78001C1.49453 9.16639 1.69867 8.54802 2.13229 8.10561C2.63127 7.59653 3.73538 7.47385 5.9436 7.22849L6.26976 7.19225C6.90189 7.12201 7.21795 7.08689 7.49423 6.96694C7.73879 6.86077 7.95965 6.70676 8.14383 6.51399C8.35189 6.29621 8.49411 6.01178 8.77855 5.44291L9.13784 4.72433Z' fill='#{$nav-white-0}' stroke='#{$nav-white-0}' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>";
1540
+ $sidenav-favorite-icon-svg: "<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path d='M11.4189 0.599609C11.8569 0.444608 12.3438 0.470222 12.7656 0.676758C13.1382 0.85937 13.3661 1.1983 13.5303 1.48926C13.7037 1.79665 13.8896 2.21226 14.1113 2.7041L16.4121 7.80957H20.7764C21.3853 7.80957 21.8928 7.80944 22.2793 7.84277C22.6369 7.87368 23.0791 7.9417 23.4111 8.22754C23.8327 8.59073 24.0486 9.13456 23.9902 9.68359C23.9441 10.116 23.6675 10.4639 23.4268 10.7266C23.1665 11.0102 22.7947 11.3503 22.3486 11.7588L18.7549 15.0479L20.415 19.7119C20.6345 20.3287 20.8155 20.837 20.9189 21.2324C21.0129 21.5918 21.1024 22.0532 20.9238 22.4707C20.7004 22.993 20.2266 23.3717 19.6621 23.4785C19.2108 23.5637 18.7714 23.3814 18.4346 23.2158C18.064 23.0337 17.5972 22.7531 17.0312 22.4131L12 19.3887L6.96875 22.4131C6.40288 22.7531 5.93606 23.0337 5.56543 23.2158C5.22855 23.3813 4.78918 23.5638 4.33789 23.4785C3.77339 23.3717 3.29963 22.993 3.07617 22.4707C2.89765 22.0532 2.9871 21.5918 3.08105 21.2324C3.18447 20.837 3.36558 20.3286 3.58496 19.7119L5.24414 15.0479L1.65137 11.7588C1.20524 11.3502 0.833466 11.0103 0.573242 10.7266C0.332409 10.4639 0.0559191 10.116 0.00976562 9.68359C-0.048592 9.13464 0.168374 8.59073 0.589844 8.22754L0.717773 8.13086C1.02957 7.92557 1.40768 7.86983 1.7207 7.84277C2.10716 7.80943 2.61462 7.80957 3.22363 7.80957H7.58789L9.88867 2.7041C10.1103 2.21236 10.2963 1.79663 10.4697 1.48926C10.6339 1.19837 10.862 0.859412 11.2344 0.676758L11.4189 0.599609ZM12.0918 1.97266C12.0325 1.94907 11.9664 1.94881 11.9072 1.97266C11.8903 1.99137 11.8406 2.05132 11.7588 2.19629C11.6253 2.43288 11.4699 2.77672 11.2363 3.29492L8.74121 8.83203C8.62278 9.09452 8.35775 9.26367 8.06641 9.26367H3.22363C2.58589 9.26367 2.15619 9.26459 1.84961 9.29102C1.65621 9.30775 1.57052 9.33069 1.54395 9.33887C1.4994 9.38481 1.47564 9.44621 1.47656 9.50977C1.4907 9.53411 1.53727 9.6094 1.66699 9.75098C1.87346 9.97601 2.18793 10.2644 2.65527 10.6924L6.60254 14.3066C6.81725 14.5033 6.89362 14.8073 6.79688 15.0801L4.97656 20.1943C4.74783 20.8372 4.59263 21.2751 4.50879 21.5957C4.45591 21.798 4.44774 21.8941 4.44629 21.9258C4.47718 21.982 4.52974 22.0236 4.5918 22.042C4.62177 22.0343 4.71557 22.0082 4.90723 21.9141C5.20782 21.7664 5.61016 21.5255 6.2002 21.1709L11.6162 17.916C11.8518 17.7744 12.1481 17.7744 12.3838 17.916L17.7998 21.1709C18.39 21.5256 18.7921 21.7664 19.0928 21.9141C19.2823 22.0072 19.3761 22.0339 19.4072 22.042C19.4695 22.0238 19.5216 21.982 19.5527 21.9258C19.5512 21.8941 19.5441 21.798 19.4912 21.5957C19.4073 21.2751 19.2522 20.8372 19.0234 20.1943L17.2031 15.0801C17.1064 14.8074 17.183 14.5034 17.3975 14.3066L21.3447 10.6924C21.8119 10.2645 22.1265 9.97601 22.333 9.75098C22.4617 9.61056 22.5079 9.53469 22.5225 9.50977C22.5233 9.44589 22.5 9.38489 22.4551 9.33887C22.4274 9.3304 22.3416 9.30754 22.1504 9.29102C21.8438 9.2646 21.414 9.26367 20.7764 9.26367H15.9336C15.6422 9.26359 15.3771 9.09454 15.2588 8.83203L12.7637 3.29492C12.53 2.7766 12.3747 2.43289 12.2412 2.19629C12.1584 2.04951 12.108 1.99058 12.0918 1.97266Z' fill='#{$nav-white-0}'/></svg>";
1541
+ $sidenav-favorite-icon-svg-filled: "<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path d='M11.4189 0.599457C11.8569 0.444456 12.3438 0.470069 12.7656 0.676605C13.1383 0.859207 13.3671 1.19809 13.5312 1.48911C13.7046 1.79644 13.8897 2.21229 14.1113 2.70395L16.4121 7.80942H20.7773C21.386 7.80942 21.8929 7.80931 22.2793 7.84262C22.6369 7.87353 23.0791 7.94253 23.4111 8.22836C23.8325 8.59154 24.0496 9.13456 23.9912 9.68344C23.9451 10.1159 23.6676 10.4637 23.4268 10.7264C23.1665 11.0101 22.7947 11.3502 22.3486 11.7586L18.7559 15.0477L20.415 19.7118C20.6345 20.3285 20.8155 20.8368 20.9189 21.2323C21.0129 21.5917 21.1024 22.053 20.9238 22.4706C20.7003 22.9929 20.2267 23.3716 19.6621 23.4784C19.2108 23.5635 18.7714 23.3812 18.4346 23.2157C18.0641 23.0336 17.598 22.7529 17.0322 22.4129L12 19.3885L6.96875 22.4129C6.40284 22.753 5.93608 23.0336 5.56543 23.2157C5.22855 23.3811 4.78918 23.5636 4.33789 23.4784C3.77348 23.3715 3.29961 22.9928 3.07617 22.4706C2.89773 22.0531 2.98711 21.5916 3.08105 21.2323C3.18447 20.8368 3.36655 20.3284 3.58594 19.7118L5.24414 15.0477L1.65137 11.7586C1.20515 11.35 0.833472 11.0102 0.573242 10.7264C0.33238 10.4637 0.0558689 10.1159 0.00976562 9.68344C-0.0485905 9.1345 0.168395 8.59155 0.589844 8.22836L0.717773 8.13071C1.02957 7.92542 1.40768 7.86968 1.7207 7.84262C2.10718 7.80926 2.61454 7.80942 3.22363 7.80942H7.58789L9.88867 2.70395C10.1103 2.21223 10.2963 1.79647 10.4697 1.48911C10.6339 1.19814 10.8628 0.859214 11.2354 0.676605L11.4189 0.599457Z' fill='#{$nav-white-0}'/></svg>";
1520
1542
  $sidenav-favorite-size: 1.25rem; /* 20px */
1521
1543
  $sidenav-link-gap: map.get($spacers, 5);
1522
1544
  $sidenav-link-segment-icon-height: 5.625rem; /* 90px */
@@ -1547,10 +1569,11 @@ body {
1547
1569
  @mixin filter-nav-orange-0 {
1548
1570
  /*
1549
1571
  * CSS workaround to change element color (including its `background-image`)
1550
- * from white to any given color, `$orange-0` in this case.
1572
+ * from white to any given color, `$orange-0-dark` in this case. Though it
1573
+ * evaluates to `#ff9938`, not the exact `#ff9933`, it is _good enough_.
1551
1574
  * Generated with: https://codepen.io/jumarjuaton/full/mdJYWYq
1552
1575
  */
1553
- filter: invert(50%) sepia(99%) saturate(1810%) hue-rotate(3deg) brightness(93%) contrast(94%);
1576
+ filter: invert(57%) sepia(100%) saturate(478%) hue-rotate(336deg) brightness(141%) contrast(101%);
1554
1577
  }
1555
1578
 
1556
1579
  @mixin filter-nav-gray-3 {
@@ -1558,7 +1581,7 @@ body {
1558
1581
  * CSS workaround to change element color (including its `background-image`) from white to `$nav-gray-3`.
1559
1582
  * Generated with: https://codepen.io/jumarjuaton/full/mdJYWYq
1560
1583
  */
1561
- filter: invert(39%) sepia(6%) saturate(1228%) hue-rotate(166deg) brightness(54%) contrast(86%);
1584
+ filter: invert(69%) sepia(15%) saturate(1%) hue-rotate(311deg) brightness(223%) contrast(98%);
1562
1585
  }
1563
1586
 
1564
1587
  @include box-shadow(0.0625rem 0 0 0 $navy-2, $box-shadow);
@@ -1655,19 +1678,20 @@ body {
1655
1678
  min-width: $sidenav-link-width;
1656
1679
 
1657
1680
  /*
1658
- * FIXME(norbert-mieczkowski-codilime): remove this Firefox hack when it fixes its bug: https://bugzilla.mozilla.org/show_bug.cgi?id=995020.
1681
+ * FIXME(norbert-mieczkowski-codilime): remove this hack when browsers fix their bug: https://bugzilla.mozilla.org/show_bug.cgi?id=995020.
1682
+ * Bug has already been observed in Firefox and Safari, Chrome is confirmed to be _clean_.
1659
1683
  * Workaround based on: https://www.answeroverflow.com/m/1353731321620598945.
1660
1684
  */
1661
- @include nb-firefox-only {
1662
- flex-direction: row;
1663
- padding-block: map.get($spacers, 0);
1664
- padding-inline: map.get($spacers, 20);
1665
- writing-mode: vertical-lr;
1685
+ /* hack */
1686
+ flex-direction: row;
1687
+ padding-block: map.get($spacers, 0);
1688
+ padding-inline: map.get($spacers, 20);
1689
+ writing-mode: vertical-lr;
1666
1690
 
1667
- > * {
1668
- writing-mode: horizontal-tb;
1669
- }
1691
+ > * {
1692
+ writing-mode: horizontal-tb;
1670
1693
  }
1694
+ /* endhack */
1671
1695
  }
1672
1696
 
1673
1697
  .nb-sidenav-link {
@@ -1683,7 +1707,7 @@ body {
1683
1707
  width: $sidenav-link-width;
1684
1708
 
1685
1709
  &-active {
1686
- color: $orange-0;
1710
+ color: $orange-0-dark;
1687
1711
  }
1688
1712
 
1689
1713
  &::before { /* Segment icon */
@@ -1818,7 +1842,7 @@ body {
1818
1842
  }
1819
1843
 
1820
1844
  &:has(+ .nb-sidenav-flyout .nb-sidenav-link-active) { /* When list item contains an active link */
1821
- color: $orange-0;
1845
+ color: $orange-0-dark;
1822
1846
 
1823
1847
  > :first-child, /* Nav menu tab icon */
1824
1848
  > :last-child::after { /* Chevron icon */
@@ -2233,11 +2257,11 @@ ul.nb-software-image-hierarchy {
2233
2257
 
2234
2258
  ul.nb-tree-hierarchy {
2235
2259
  list-style-type: none;
2236
- padding-block-start: map.get($spacers, 0);
2260
+ padding-inline-start: map.get($spacers, 0);
2237
2261
 
2238
2262
  ul {
2239
2263
  list-style-type: "↳ ";
2240
- padding-block-start: map.get($spacers, 4);
2264
+ padding-inline-start: 1.125rem; /* 18px */
2241
2265
  }
2242
2266
  }
2243
2267
 
@@ -2436,3 +2460,12 @@ pre code.hljs {
2436
2460
  font-size: 1em;
2437
2461
  }
2438
2462
  }
2463
+
2464
+ .nb-lg-max-width {
2465
+ max-width: 960px;
2466
+ }
2467
+
2468
+ /* Workaround to fix `d-none` being overridden by `d-md-flex` (and possibly other `d-{display}` classes). */
2469
+ .d-none {
2470
+ display: none !important;
2471
+ }
@@ -2,16 +2,49 @@
2
2
  {% load helpers %}
3
3
 
4
4
  {% block usercontent %}
5
- <small class="text-secondary">User login</small>
6
- <h5>{{ request.user.username }}</h5>
7
- <small class="text-secondary">Full name</small>
8
- <h5>{{ request.user.first_name }} {{ request.user.last_name }}</h5>
9
- <small class="text-secondary">Email</small>
10
- <h5>{{ request.user.email }}</h5>
11
- <small class="text-secondary">Registered</small>
12
- <h5>{{ request.user.date_joined }}</h5>
13
- <small class="text-secondary">Groups</small>
14
- <h5>{{ request.user.groups.all|join:', ' }}</h5>
15
- <small class="text-secondary">Admin access</small>
16
- <h5>{{ request.user.is_staff|yesno|capfirst }}</h5>
5
+ <div class="row align-content-start flex-fill">
6
+ <div class="col-md-8 offset-md-2">
7
+ <div class="card">
8
+ <div class="card-header">
9
+ <strong>User Profile</strong>
10
+ </div>
11
+ <div class="collapse show">
12
+ <table class="table table-hover attr-table">
13
+ <tr>
14
+ <td>User login</td>
15
+ <td>{{ request.user.username }}</td>
16
+ </tr>
17
+ <tr>
18
+ <td>Full name</td>
19
+ {% if request.user.first_name or request.user.last_name %}
20
+ <td>{{ request.user.first_name }} {{ request.user.last_name }}</td>
21
+ {% else %}
22
+ <td>{{ None|placeholder }}</td>
23
+ {% endif %}
24
+ </tr>
25
+ <tr>
26
+ <td>Email</td>
27
+ <td>{{ request.user.email|hyperlinked_email }}</td>
28
+ </tr>
29
+ <tr>
30
+ <td>Registered</td>
31
+ <td>{{ request.user.date_joined|date:settings.SHORT_DATETIME_FORMAT }}</td>
32
+ </tr>
33
+ <tr>
34
+ <td>Groups</td>
35
+ <td>{{ request.user.groups.all|join:', '|placeholder }}</td>
36
+ </tr>
37
+ <tr>
38
+ <td>Admin access</td>
39
+ <td>{{ request.user.is_staff|render_boolean }}</td>
40
+ </tr>
41
+ <tr>
42
+ <td>Superuser access</td>
43
+ <td>{{ request.user.is_superuser|render_boolean }}</td>
44
+ </tr>
45
+ </table>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </div>
17
50
  {% endblock %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_delete.html' %}
1
+ {% extends 'generic/object_destroy.html' %}
2
2
 
3
3
  {% block message %}
4
4
  <p>Are you sure you want to delete your session key?</p>
@@ -348,6 +348,10 @@ class TokenTest(APIViewTestCases.APIViewTestCase):
348
348
 
349
349
  class ObjectPermissionTest(APIViewTestCases.APIViewTestCase):
350
350
  model = ObjectPermission
351
+ validation_excluded_fields = [
352
+ "groups", # M2M field, excluded by default
353
+ "users", # M2M field, excluded by default
354
+ ]
351
355
 
352
356
  @classmethod
353
357
  def setUpTestData(cls):
nautobot/users/views.py CHANGED
@@ -64,6 +64,7 @@ class LoginView(View):
64
64
  self.template_name,
65
65
  {
66
66
  "form": form,
67
+ "title": "Login",
67
68
  },
68
69
  )
69
70
 
@@ -91,6 +92,7 @@ class LoginView(View):
91
92
  self.template_name,
92
93
  {
93
94
  "form": form,
95
+ "title": "Login",
94
96
  },
95
97
  )
96
98
 
@@ -402,7 +404,7 @@ class TokenDeleteView(GenericView):
402
404
 
403
405
  return render(
404
406
  request,
405
- "generic/object_delete.html",
407
+ "generic/object_destroy.html",
406
408
  {
407
409
  "obj": token,
408
410
  "obj_type": token._meta.verbose_name,
@@ -421,7 +423,7 @@ class TokenDeleteView(GenericView):
421
423
 
422
424
  return render(
423
425
  request,
424
- "generic/object_delete.html",
426
+ "generic/object_destroy.html",
425
427
  {
426
428
  "obj": token,
427
429
  "obj_type": token._meta.verbose_name,
@@ -1,6 +1,6 @@
1
1
  from django.contrib.contenttypes.models import ContentType
2
2
  from django.core.exceptions import ValidationError
3
- from django.db import models, transaction
3
+ from django.db import models
4
4
 
5
5
  from nautobot.core.constants import CHARFIELD_MAX_LENGTH
6
6
  from nautobot.core.models import BaseManager
@@ -382,73 +382,6 @@ class VMInterface(PrimaryModel, BaseInterface):
382
382
 
383
383
  return super().to_objectchange(action, related_object=virtual_machine, **kwargs)
384
384
 
385
- def add_ip_addresses(
386
- self,
387
- ip_addresses,
388
- is_source=False,
389
- is_destination=False,
390
- is_default=False,
391
- is_preferred=False,
392
- is_primary=False,
393
- is_secondary=False,
394
- is_standby=False,
395
- ):
396
- """Add one or more IPAddress instances to this interface's `ip_addresses` many-to-many relationship.
397
-
398
- Args:
399
- ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
400
- is_source (bool, optional): Is source address. Defaults to False.
401
- is_destination (bool, optional): Is destination address. Defaults to False.
402
- is_default (bool, optional): Is default address. Defaults to False.
403
- is_preferred (bool, optional): Is preferred address. Defaults to False.
404
- is_primary (bool, optional): Is primary address. Defaults to False.
405
- is_secondary (bool, optional): Is secondary address. Defaults to False.
406
- is_standby (bool, optional): Is standby address. Defaults to False.
407
-
408
- Returns:
409
- Number of instances added.
410
- """
411
- if not isinstance(ip_addresses, (tuple, list)):
412
- ip_addresses = [ip_addresses]
413
- with transaction.atomic():
414
- for ip in ip_addresses:
415
- instance = self.ip_addresses.through(
416
- ip_address=ip,
417
- vm_interface=self,
418
- is_source=is_source,
419
- is_destination=is_destination,
420
- is_default=is_default,
421
- is_preferred=is_preferred,
422
- is_primary=is_primary,
423
- is_secondary=is_secondary,
424
- is_standby=is_standby,
425
- )
426
- instance.validated_save()
427
- return len(ip_addresses)
428
-
429
- add_ip_addresses.alters_data = True
430
-
431
- def remove_ip_addresses(self, ip_addresses):
432
- """Remove one or more IPAddress instances from this interface's `ip_addresses` many-to-many relationship.
433
-
434
- Args:
435
- ip_addresses (:obj:`list` or `IPAddress`): Instance of `nautobot.ipam.models.IPAddress` or list of `IPAddress` instances.
436
-
437
- Returns:
438
- Number of instances removed.
439
- """
440
- count = 0
441
- if not isinstance(ip_addresses, (tuple, list)):
442
- ip_addresses = [ip_addresses]
443
- with transaction.atomic():
444
- for ip in ip_addresses:
445
- qs = self.ip_addresses.through.objects.filter(ip_address=ip, vm_interface=self)
446
- deleted_count, _ = qs.delete()
447
- count += deleted_count
448
- return count
449
-
450
- remove_ip_addresses.alters_data = True
451
-
452
385
  @property
453
386
  def parent(self):
454
387
  return self.virtual_machine
@@ -4,12 +4,13 @@ from nautobot.core.apps import (
4
4
  NavMenuItem,
5
5
  NavMenuTab,
6
6
  )
7
+ from nautobot.core.ui.choices import NavigationIconChoices, NavigationWeightChoices
7
8
 
8
9
  menu_items = (
9
10
  NavMenuTab(
10
11
  name="Virtualization",
11
- icon="cloud-upload",
12
- weight=400,
12
+ icon=NavigationIconChoices.VIRTUALIZATION,
13
+ weight=NavigationWeightChoices.VIRTUALIZATION,
13
14
  groups=(
14
15
  NavMenuGroup(
15
16
  name="Virtual Machines",
@@ -1,3 +1,3 @@
1
- {% extends 'generic/object_delete.html' %}
1
+ {% extends 'generic/object_destroy.html' %}
2
2
 
3
3
  {% block message_extra %}This would also delete any/all child VMInterfaces of this VMInterface.{% endblock %}
@@ -1,2 +1,2 @@
1
- {% extends 'virtualization/virtualmachine_retrieve.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -2,12 +2,12 @@
2
2
 
3
3
  {% block bulk_buttons %}
4
4
  {% if perms.virtualization.change_virtualmachine %}
5
- <div class="btn-group">
5
+ <div class="dropdown d-inline-flex">
6
6
  <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
7
7
  <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="mdi mdi-chevron-down"></span>
8
8
  </button>
9
9
  <ul class="dropdown-menu">
10
- {% if perms.virtualization.add_vminterface %}<li><a href="{% url 'virtualization:virtualmachine_bulk_add_vminterface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Interfaces</a></li>{% endif %}
10
+ {% if perms.virtualization.add_vminterface %}<li><a href="{% url 'virtualization:virtualmachine_bulk_add_vminterface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item formaction">Interfaces</a></li>{% endif %}
11
11
  </ul>
12
12
  </div>
13
13
  {% endif %}
@@ -31,7 +31,9 @@
31
31
  {% render_field form.primary_ip4 %}
32
32
  {% render_field form.primary_ip6 %}
33
33
  {% render_field form.software_version %}
34
- <div class="mb-10 d-flex justify-content-center" id="software_version_image_container" style="display:none">
34
+ <div class="mb-10 d-flex justify-content-center"
35
+ id="software_version_image_container"
36
+ style="display:none !important">{# !important is required to override d-flex #}
35
37
  <div class="col-lg-3"></div>
36
38
  <div class="col-lg-9">
37
39
  <span class="form-text">
@@ -281,6 +281,9 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
281
281
  "description": "New description",
282
282
  }
283
283
  choices_fields = ["mode"]
284
+ validation_excluded_fields = [
285
+ "tagged_vlans", # M2M field, excluded by default
286
+ ]
284
287
 
285
288
  @classmethod
286
289
  def setUpTestData(cls):