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
@@ -2,38 +2,6 @@
2
2
  {% load helpers %}
3
3
  {% load static %}
4
4
 
5
- {% block title %}{{ object }} - Change Log{% endblock %}
6
-
7
- {% block header %}
8
- <div class="row d-print-none">
9
- <div class="col-md-8 col-lg-9">
10
- <ol class="breadcrumb">
11
- <li class="breadcrumb-item"><a href="{% url 'extras:objectchange_list' %}">Change Log</a></li>
12
- {% if object.related_object.get_absolute_url %}
13
- <li class="breadcrumb-item"><a href="{{ object.related_object.get_absolute_url }}changelog/">{{ object.related_object }}</a></li>
14
- {% elif object.changed_object and object.changed_object.get_absolute_url %}
15
- <li class="breadcrumb-item"><a href="{{ object.changed_object.get_absolute_url }}changelog/">{{ object.changed_object }}</a></li>
16
- {% elif object.changed_object is not None %}
17
- <li class="breadcrumb-item">{{ object.changed_object }}</li>
18
- {% endif %}
19
- <li class="breadcrumb-item">{{ object }}</li>
20
- </ol>
21
- </div>
22
- <div class="col-md-4 col-lg-3">
23
- <form action="{% url 'extras:objectchange_list' %}" method="get">
24
- <div class="input-group">
25
- <input type="text" name="q" class="form-control" placeholder="Search change logs" />
26
- <span class="input-group-btn">
27
- <button type="submit" class="btn btn-primary">
28
- <span class="mdi mdi-magnify" aria-hidden="true"></span>
29
- </button>
30
- </span>
31
- </div>
32
- </form>
33
- </div>
34
- </div>
35
- {% endblock %}
36
-
37
5
  {% block content %}
38
6
  <div class="row">
39
7
  <div class="col-lg-5">
@@ -113,13 +81,25 @@
113
81
  <div class="card">
114
82
  <div class="card-header">
115
83
  <strong>Difference</strong>
116
- <div class="btn-group btn-group-xs float-end d-print-none">
117
- <a {% if prev_change %}href="{% url 'extras:objectchange' pk=prev_change.pk %}"{% else %}disabled{% endif %} class="btn btn-secondary">
118
- <span class="mdi mdi-chevron-left" aria-hidden="true"></span> Previous
119
- </a>
120
- <a {% if next_change %}href="{% url 'extras:objectchange' pk=next_change.pk %}"{% else %}disabled{% endif %} class="btn btn-secondary">
121
- Next <span class="mdi mdi-chevron-right" aria-hidden="true"></span>
122
- </a>
84
+ <div class="btn-group btn-group-sm float-end d-print-none" role="group">
85
+ {% if prev_change %}
86
+ <a href="{% url 'extras:objectchange' pk=prev_change.pk %}" class="btn btn-secondary">
87
+ <span class="mdi mdi-chevron-left" aria-hidden="true"></span> Previous
88
+ </a>
89
+ {% else %}
90
+ <a aria-disabled="true" class="btn btn-secondary disabled">
91
+ <span class="mdi mdi-chevron-left" aria-hidden="true"></span> Previous
92
+ </a>
93
+ {% endif %}
94
+ {% if next_change %}
95
+ <a href="{% url 'extras:objectchange' pk=next_change.pk %}" class="btn btn-secondary">
96
+ Next <span class="mdi mdi-chevron-right" aria-hidden="true"></span>
97
+ </a>
98
+ {% else %}
99
+ <a aria-disabled="true" class="btn btn-secondary disabled">
100
+ Next <span class="mdi mdi-chevron-right" aria-hidden="true"></span>
101
+ </a>
102
+ {% endif %}
123
103
  </div>
124
104
  </div>
125
105
  <div class="card-body">
@@ -4,29 +4,34 @@
4
4
 
5
5
  {% block breadcrumbs_wrapper %}{% render_breadcrumbs %}{% endblock breadcrumbs_wrapper %}
6
6
 
7
- {% block header %}
8
- <div class="float-end d-print-none">
9
- {% block buttons %}
10
- {% if app_data.home_url %}
11
- <a href="{% url app_data.home_url %}" class="btn btn-primary">
12
- <span class="mdi mdi-home"></span> App Home
13
- </a>
14
- {% endif %}
15
- {% if app_data.config_url %}
16
- <a href="{% url app_data.config_url %}" class="btn btn-warning">
17
- <span class="mdi mdi-cog"></span> App Configuration
18
- </a>
19
- {% endif %}
20
- {% if app_data.docs_url %}
21
- <a href="{% url app_data.docs_url %}" class="btn btn-info">
22
- <span class="mdi mdi-cog"></span> App Documentation
23
- </a>
24
- {% endif %}
25
- {% endblock buttons %}
26
- </div>
27
- {% endblock header %}
7
+ {% block header %}{% endblock header %}
28
8
 
29
9
  {% block content %}
10
+
11
+ <div class="row d-print-none mb-16">
12
+ <div class="col-lg-12">
13
+ <div class="float-end d-print-none">
14
+ {% block buttons %}
15
+ {% if app_data.home_url %}
16
+ <a href="{% url app_data.home_url %}" class="btn btn-primary">
17
+ <span class="mdi mdi-home"></span> App Home
18
+ </a>
19
+ {% endif %}
20
+ {% if app_data.config_url %}
21
+ <a href="{% url app_data.config_url %}" class="btn btn-warning">
22
+ <span class="mdi mdi-cog"></span> App Configuration
23
+ </a>
24
+ {% endif %}
25
+ {% if app_data.docs_url %}
26
+ <a href="{% url app_data.docs_url %}" class="btn btn-info">
27
+ <span class="mdi mdi-cog"></span> App Documentation
28
+ </a>
29
+ {% endif %}
30
+ {% endblock buttons %}
31
+ </div>
32
+ </div>
33
+ </div>
34
+
30
35
  <div class="row">
31
36
  <div class="col-lg-6">
32
37
  {% block content_left_page %}
@@ -65,7 +70,7 @@
65
70
  {% if app_data.availability %}
66
71
  <tr>
67
72
  <td>License</td>
68
- <td><label class="badge bg-transparent">{{ app_data.availability }}</label></td>
73
+ <td><label class="badge border border-primary text-primary">{{ app_data.availability }}</label></td>
69
74
  </tr>
70
75
  {% endif %}
71
76
  </table>
@@ -77,11 +82,11 @@
77
82
  <table class="table table-hover card-body attr-table">
78
83
  <tr>
79
84
  <td>Min Nautobot Version</td>
80
- <td>v{{ object.min_version | placeholder }}</td>
85
+ <td>{% if object.min_version %}v{{ object.min_version }}{% else %}{{ None|placeholder }}{% endif %}</td>
81
86
  </tr>
82
87
  <tr>
83
88
  <td>Max Nautobot Version</td>
84
- <td>v{{ object.max_version | placeholder }}</td>
89
+ <td>{% if object.max_version %}v{{ object.max_version }}{% else %}{{ None|placeholder }}{% endif %}</td>
85
90
  </tr>
86
91
  </table>
87
92
  </div>
@@ -8,27 +8,26 @@
8
8
  {% endblock %}
9
9
 
10
10
  {% block content %}
11
- <div class="row d-print-none">
11
+ <div class="row d-print-none mb-16">
12
12
  <div class="col-lg-12">
13
- {% render_breadcrumbs %}
14
- </div>
15
- </div>
13
+ <div class="float-end d-print-none">
14
+ <div class="btn-group" role="group">
15
+ <a href="{% django_querystring display='list' %}"
16
+ class="btn btn-primary{% if display == 'list' %} bg-primary nb-text-body-bg{% endif %}">
17
+ <span class="mdi mdi-view-sequential" aria-hidden="true"></span> List
18
+ </a>
19
+ {# djlint:off B123 #}
20
+ <a href="{% django_querystring display='tiles' %}"
21
+ class="btn btn-primary{% if display == 'tiles' %} bg-primary nb-text-body-bg{% endif %}">
22
+ <span class="mdi mdi-view-grid" aria-hidden="true"></span> Tiles
23
+ </a>
24
+ {# djlint:on B123 #}
25
+ </div>
16
26
 
17
- <div class="float-end d-print-none">
18
- <div class="btn-group">
19
- <a class="btn btn-secondary set-apps-display" data-display="tiles" href="" title="Tiles">
20
- <span class="mdi mdi-view-grid{% if display == "list" %}-outline{% endif %}"></span>
21
- </a>
22
- <a class="btn btn-secondary set-apps-display" data-display="list" href="" title="List">
23
- <span class="mdi mdi-view-sequential{% if display == "tiles" %}-outline{% endif %}"></span>
24
- </a>
27
+ <a class="btn btn-primary" href="{% url 'apps:apps_marketplace' %}">Visit Apps Marketplace</a>
28
+ </div>
25
29
  </div>
26
-
27
- <a class="btn btn-primary" href="{% url 'apps:apps_marketplace' %}">Visit Apps Marketplace</a>
28
30
  </div>
29
-
30
- <h1>{% block page_heading %}{% render_title %}{% endblock %}</h1>
31
-
32
31
  <div class="row">
33
32
  <div class="col-lg-12">
34
33
  {% if filter_form %}
@@ -47,13 +46,4 @@
47
46
  {% block javascript %}
48
47
  {{ block.super }}
49
48
  <script src="{% static 'js/tableconfig.js' %}"></script>
50
-
51
- <script>
52
- // Set display anchor links without losing other potentially active query params such as applied filters.
53
- [...document.querySelectorAll('.set-apps-display')].forEach(button => {
54
- const queryParams = new URLSearchParams(window.location.search);
55
- queryParams.set('display', button.dataset.display);
56
- button.setAttribute('href', `?${queryParams.toString()}`);
57
- });
58
- </script>
59
49
  {% endblock %}
@@ -223,4 +223,68 @@
223
223
  </div>
224
224
  {% include 'inc/paginator.html' with paginator=vdc_table.paginator page=vdc_table.page %}
225
225
  {% endif %}
226
+ {% if vpn_table %}
227
+ <div class="card">
228
+ <div class="card-header">
229
+ <strong>VPNs</strong>
230
+ </div>
231
+ {% include 'inc/table.html' with table=vpn_table %}
232
+ {% if perms.vpn.add_vpn %}
233
+ <div class="card-footer text-end d-print-none">
234
+ <a href="{% url 'vpn:vpn_add' %}?role={{ object.pk }}" class="btn btn-sm btn-primary">
235
+ <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add VPN
236
+ </a>
237
+ </div>
238
+ {% endif %}
239
+ </div>
240
+ {% include 'inc/paginator.html' with paginator=vpn_table.paginator page=vpn_table.page %}
241
+ {% endif %}
242
+ {% if vpn_profile_table %}
243
+ <div class="card">
244
+ <div class="card-header">
245
+ <strong>VPN Profiles</strong>
246
+ </div>
247
+ {% include 'inc/table.html' with table=vpn_profile_table %}
248
+ {% if perms.vpn.add_vpnprofile %}
249
+ <div class="card-footer text-end d-print-none">
250
+ <a href="{% url 'vpn:vpnprofile_add' %}?role={{ object.pk }}" class="btn btn-sm btn-primary">
251
+ <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add VPN Profile
252
+ </a>
253
+ </div>
254
+ {% endif %}
255
+ </div>
256
+ {% include 'inc/paginator.html' with paginator=vpn_profile_table.paginator page=vpn_profile_table.page %}
257
+ {% endif %}
258
+ {% if vpn_tunnel_table %}
259
+ <div class="card">
260
+ <div class="card-header">
261
+ <strong>VPN Tunnels</strong>
262
+ </div>
263
+ {% include 'inc/table.html' with table=vpn_tunnel_table %}
264
+ {% if perms.vpn.add_vpntunnel %}
265
+ <div class="card-footer text-end d-print-none">
266
+ <a href="{% url 'vpn:vpntunnel_add' %}?role={{ object.pk }}" class="btn btn-sm btn-primary">
267
+ <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add VPN Tunnel
268
+ </a>
269
+ </div>
270
+ {% endif %}
271
+ </div>
272
+ {% include 'inc/paginator.html' with paginator=vpn_tunnel_table.paginator page=vpn_tunnel_table.page %}
273
+ {% endif %}
274
+ {% if vpn_tunnel_endpoint_table %}
275
+ <div class="card">
276
+ <div class="card-header">
277
+ <strong>VPN Tunnel Endpoints</strong>
278
+ </div>
279
+ {% include 'inc/table.html' with table=vpn_tunnel_endpoint_table %}
280
+ {% if perms.vpn.add_vpntunnelendpoint %}
281
+ <div class="card-footer text-end d-print-none">
282
+ <a href="{% url 'vpn:vpntunnelendpoint_add' %}?role={{ object.pk }}" class="btn btn-sm btn-primary">
283
+ <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add VPN Tunnel Endpoint
284
+ </a>
285
+ </div>
286
+ {% endif %}
287
+ </div>
288
+ {% include 'inc/paginator.html' with paginator=vpn_tunnel_endpoint_table.paginator page=vpn_tunnel_endpoint_table.page %}
289
+ {% endif %}
226
290
  {% endblock content_full_width_page %}
@@ -102,7 +102,8 @@
102
102
  <td>
103
103
  {{ object.start_time|timezone:object.time_zone|date:settings.SHORT_DATETIME_FORMAT }}
104
104
  {% if default_time_zone != object.time_zone %}
105
- <br>{{ object.start_time|timezone:default_time_zone|date:settings.SHORT_DATETIME_FORMAT }}
105
+ {{ object.time_zone }}
106
+ <br>{{ object.start_time|timezone:default_time_zone|date:settings.SHORT_DATETIME_FORMAT }} {{ default_time_zone }}
106
107
  {% endif %}
107
108
  </td>
108
109
  </tr>
@@ -112,7 +113,8 @@
112
113
  {% if object.last_run_at %}
113
114
  {{ object.last_run_at|timezone:object.time_zone|date:settings.SHORT_DATETIME_FORMAT }}
114
115
  {% if default_time_zone != object.time_zone %}
115
- <br>{{ object.last_run_at|timezone:default_time_zone|date:SHORT_DATETIME_FORMAT }}
116
+ {{ object.time_zone }}
117
+ <br>{{ object.last_run_at|timezone:default_time_zone|date:settings.SHORT_DATETIME_FORMAT }} {{ default_time_zone }}
116
118
  {% endif %}
117
119
  {% else %}
118
120
  {{ object.last_run_at|placeholder }}
@@ -13,7 +13,7 @@
13
13
  <div class="card">
14
14
  <div class="card-header"><strong>Parameters</strong></div>
15
15
  <div class="card-body">
16
- <span class="form-text">
16
+ <span class="form-text mb-12">
17
17
  All parameter values can be entered as Jinja2 templates if desired.
18
18
  The requesting object (device, Git repository, etc.), if any, will be provided
19
19
  as context variable <code>obj</code>.
@@ -10,16 +10,16 @@ from nautobot.extras.models import CustomLink
10
10
 
11
11
  register = template.Library()
12
12
 
13
- LINK_BUTTON = '<a href="{}"{} class="btn btn-sm btn-{}">{}</a>\n'
13
+ LINK_BUTTON = '<a href="{}"{} class="btn btn-{}">{}</a>\n'
14
14
  GROUP_BUTTON = (
15
- '<div class="btn-group">\n'
16
- '<button type="button" class="btn btn-sm btn-{} dropdown-toggle" data-toggle="dropdown">\n'
17
- '{} <span class="caret"></span>\n'
15
+ '<div class="dropdown d-inline-flex align-middle">\n'
16
+ '<button type="button" class="btn btn-{} dropdown-toggle" data-bs-toggle="dropdown">\n'
17
+ '{} <span class="mdi mdi-chevron-down" aria-hidden="true"></span>\n'
18
18
  "</button>\n"
19
- '<ul class="dropdown-menu pull-right">\n'
19
+ '<ul class="dropdown-menu float-end">\n'
20
20
  "{}</ul></div>\n"
21
21
  )
22
- GROUP_LINK = '<li><a href="{}"{}>{}</a></li>\n'
22
+ GROUP_LINK = '<li><a class="dropdown-item" href="{}"{}>{}</a></li>\n'
23
23
 
24
24
 
25
25
  @register.simple_tag(takes_context=True)
@@ -58,12 +58,12 @@ def custom_links(context, obj):
58
58
  link_rendered = render_jinja2(cl.target_url, link_context)
59
59
  link_target = ' target="_blank"' if cl.new_window else ""
60
60
  template_code += format_html(
61
- LINK_BUTTON, link_rendered, link_target, cl.button_class, text_rendered
61
+ LINK_BUTTON, link_rendered, link_target, cl.button_class_css_class, text_rendered
62
62
  )
63
63
  except Exception as e:
64
64
  template_code += format_html(
65
- '<a class="btn btn-sm btn-default" disabled="disabled" title="{}">'
66
- '<i class="mdi mdi-alert"></i> {}</a>\n',
65
+ '<a aria-disabled="true" class="btn btn-secondary disabled" title="{}">'
66
+ '<span class="mdi mdi-alert"></span> {}</a>\n',
67
67
  e,
68
68
  cl.name,
69
69
  )
@@ -81,13 +81,13 @@ def custom_links(context, obj):
81
81
  links_rendered += format_html(GROUP_LINK, link_rendered, link_target, text_rendered)
82
82
  except Exception as e:
83
83
  links_rendered += format_html(
84
- '<li><a disabled="disabled" title="{}"><span class="text-secondary">'
85
- '<i class="mdi mdi-alert"></i> {}</span></a></li>',
84
+ '<li><a aria-disabled="true" class="disabled dropdown-item" title="{}"><span class="text-secondary">'
85
+ '<span class="mdi mdi-alert"></span> {}</span></a></li>',
86
86
  e,
87
87
  cl.name,
88
88
  )
89
89
 
90
90
  if links_rendered:
91
- template_code += format_html(GROUP_BUTTON, links[0].button_class, group, links_rendered)
91
+ template_code += format_html(GROUP_BUTTON, links[0].button_class_css_class, group, links_rendered)
92
92
 
93
93
  return template_code
@@ -13,11 +13,11 @@ from nautobot.extras.models import Job, JobButton, JobQueue
13
13
  register = template.Library()
14
14
 
15
15
  GROUP_DROPDOWN = """
16
- <div class="btn-group">
17
- <button type="button" class="btn btn-sm btn-{group_button_class} dropdown-toggle" data-toggle="dropdown">
18
- {group_name} <span class="caret"></span>
16
+ <div class="dropdown d-inline-flex align-middle">
17
+ <button type="button" class="btn btn-{group_button_class} dropdown-toggle" data-bs-toggle="dropdown">
18
+ {group_name} <span class="mdi mdi-chevron-down" aria-hidden="true"></span>
19
19
  </button>
20
- <ul class="dropdown-menu pull-right">
20
+ <ul class="dropdown-menu dropdown-menu-end">
21
21
  {grouped_buttons}
22
22
  </ul>
23
23
  </div>
@@ -33,7 +33,7 @@ HIDDEN_INPUTS = """
33
33
  """
34
34
 
35
35
  NO_CONFIRM_BUTTON = """
36
- <button type="submit" form="form_id_{button_id}" class="btn btn-sm btn-{button_class}" {disabled}>{button_text}</button>
36
+ <button type="submit" form="form_id_{button_id}" class="btn btn-{button_class} {menu_item}" {disabled}>{button_text}</button>
37
37
  """
38
38
 
39
39
  NO_CONFIRM_FORM = """
@@ -43,7 +43,7 @@ NO_CONFIRM_FORM = """
43
43
  """
44
44
 
45
45
  CONFIRM_BUTTON = """
46
- <button type="button" class="btn btn-sm btn-{button_class}" data-toggle="modal" data-target="#confirm_modal_id_{button_id}" {disabled}>
46
+ <button type="button" class="btn btn-{button_class} {menu_item}" data-bs-toggle="modal" data-bs-target="#confirm_modal_id_{button_id}" {disabled}>
47
47
  {button_text}
48
48
  </button>
49
49
  """
@@ -53,8 +53,8 @@ CONFIRM_MODAL = """
53
53
  <div class="modal-dialog" role="document">
54
54
  <div class="modal-content">
55
55
  <div class="modal-header">
56
- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
57
56
  <h4 class="modal-title" id="confirm_modal_label_{button_id}">Confirmation</h4>
57
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
58
58
  </div>
59
59
  <form id="form_id_{button_id}" action="{button_url}" method="post" class="form">
60
60
  <div class="modal-body">
@@ -62,7 +62,7 @@ CONFIRM_MODAL = """
62
62
  Run Job <strong>'{job}'</strong> with object <strong>'{object}'</strong>?
63
63
  </div>
64
64
  <div class="modal-footer">
65
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
65
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
66
66
  <button type="submit" class="btn btn-primary">Confirm</button>
67
67
  </div>
68
68
  </form>
@@ -94,8 +94,9 @@ def _render_job_button_for_obj(job_button, obj, context, content_type):
94
94
  except Exception as exc:
95
95
  return (
96
96
  format_html(
97
- '<a class="btn btn-sm btn-{}" disabled="disabled" title="{}"><i class="mdi mdi-alert"></i> {}</a>\n',
98
- "default" if not job_button.group_name else "link",
97
+ '<a aria-disabled="true" class="btn btn-{} disabled" title="{}">'
98
+ '<span class="mdi mdi-alert"></span> {}</a>\n',
99
+ "secondary" if not job_button.group_name else "link dropdown-item",
99
100
  exc,
100
101
  job_button.name,
101
102
  ),
@@ -123,12 +124,13 @@ def _render_job_button_for_obj(job_button, obj, context, content_type):
123
124
  template_args = {
124
125
  "button_id": job_button.pk,
125
126
  "button_text": text_rendered,
126
- "button_class": job_button.button_class if not job_button.group_name else "link",
127
+ "button_class": job_button.button_class_css_class if not job_button.group_name else "link",
127
128
  "button_url": reverse("extras:job_run", kwargs={"pk": job_button.job.pk}),
128
129
  "object": obj,
129
130
  "job": job_button.job,
130
131
  "hidden_inputs": hidden_inputs,
131
132
  "disabled": "" if (has_run_perm and job_button.job.installed and job_button.job.enabled) else "disabled",
133
+ "menu_item": "dropdown-item" if job_button.group_name else "",
132
134
  }
133
135
 
134
136
  if job_button.confirmation:
@@ -170,7 +172,7 @@ def job_buttons(context, obj):
170
172
 
171
173
  # Add grouped buttons to template
172
174
  for group_name, buttons in group_names.items():
173
- group_button_class = buttons[0].button_class
175
+ group_button_class = buttons[0].button_class_css_class
174
176
 
175
177
  buttons_rendered = SAFE_EMPTY_STR
176
178
 
@@ -0,0 +1,9 @@
1
+ from nautobot.core.celery import register_jobs
2
+ from nautobot.extras.jobs import Job, no_such_attribute # noqa: F401 # pylint: disable=no-name-in-module
3
+
4
+
5
+ class MyJob(Job):
6
+ pass
7
+
8
+
9
+ register_jobs(MyJob)
@@ -0,0 +1,23 @@
1
+ from nautobot.core.celery import register_jobs
2
+ from nautobot.extras.jobs import get_task_logger, Job
3
+
4
+ logger = get_task_logger(__name__)
5
+
6
+
7
+ class TestLogCountsByLevel(Job):
8
+ class Meta:
9
+ description = "Test counts of log entries by level."
10
+
11
+ def run(self): # pylint: disable=arguments-differ
12
+ # intentionally omit debug
13
+ logger.info("This is an info log")
14
+ logger.info("This is an info log")
15
+ logger.warning("This is a warning log")
16
+ logger.warning("This is a warning log")
17
+ logger.error("This is an error log")
18
+ logger.critical("This is a critical log")
19
+ logger.failure("This is a failure log")
20
+ # success log will be added by post_run
21
+
22
+
23
+ register_jobs(TestLogCountsByLevel)
@@ -0,0 +1,11 @@
1
+ from no_such_module import no_such_attribute # noqa: F401 # pylint: disable=import-error
2
+
3
+ from nautobot.core.celery import register_jobs
4
+ from nautobot.extras.jobs import Job
5
+
6
+
7
+ class MyJob(Job):
8
+ pass
9
+
10
+
11
+ register_jobs(MyJob)
@@ -1,15 +1,14 @@
1
1
  from django.contrib.contenttypes.models import ContentType
2
- from django.test import tag
3
2
  from django.urls import reverse
4
3
 
5
- from nautobot.core.testing.integration import SeleniumTestCase
4
+ from nautobot.core.testing.integration import ObjectDetailsMixin, ObjectsListMixin, SeleniumTestCase
6
5
  from nautobot.dcim.models import Device
7
6
  from nautobot.extras.models import ComputedField
8
7
 
9
8
  from . import create_test_device
10
9
 
11
10
 
12
- class ComputedFieldsTestCase(SeleniumTestCase):
11
+ class ComputedFieldsTestCase(SeleniumTestCase, ObjectDetailsMixin, ObjectsListMixin):
13
12
  """
14
13
  Integration test to check nautobot.extras.models.ComputedField.advanced_ui functionality
15
14
  and computed fields appearing in an object list
@@ -26,7 +25,6 @@ class ComputedFieldsTestCase(SeleniumTestCase):
26
25
  template="{{ obj.name }} is awesome!",
27
26
  )
28
27
 
29
- @tag("fix_in_v3")
30
28
  def test_computed_field_advanced_ui(self):
31
29
  """
32
30
  This test creates a device and a computed field for that device.
@@ -41,7 +39,7 @@ class ComputedFieldsTestCase(SeleniumTestCase):
41
39
  self.assertTrue(self.browser.is_text_present("Device Computed Field"))
42
40
  self.assertTrue(self.browser.is_text_present(f"{self.device.name} is awesome!"))
43
41
  # # Check the computed field does NOT appear in the advanced tab
44
- self.browser.links.find_by_partial_text("Advanced")[0].click()
42
+ self.switch_tab("Advanced")
45
43
  self.assertFalse(self.browser.is_text_present("Device Computed Field"))
46
44
  self.assertFalse(self.browser.is_text_present(f"{self.device.name} is awesome!"))
47
45
  # Set the custom_field to only show in the advanced tab
@@ -53,18 +51,20 @@ class ComputedFieldsTestCase(SeleniumTestCase):
53
51
  self.assertFalse(self.browser.is_text_present("Device Computed Field"))
54
52
  self.assertFalse(self.browser.is_text_present(f"{self.device.name} is awesome!"))
55
53
  # Check the computed field appears in the advanced tab
56
- self.browser.links.find_by_partial_text("Advanced")[0].click()
54
+ self.switch_tab("Advanced")
57
55
  self.assertTrue(self.browser.is_text_present("Device Computed Field"))
58
56
  self.assertTrue(self.browser.is_text_present(f"{self.device.name} is awesome!"))
59
57
 
60
- @tag("fix_in_v3")
61
58
  def test_computed_field_appears_in_object_list(self):
62
59
  """
63
60
  This test sets the computed field to be visible on the object list
64
61
  and then checks to see if it actually appears there.
65
62
  """
66
63
  self.browser.visit(f"{self.live_server_url}/dcim/devices/")
67
- self.click_button("#table-configure-button")
64
+ self.click_button("""button[title="Configure table"]""")
65
+ nb_drawer_open_xpath = "//section[contains(@class, 'nb-drawer') and contains(@class, 'nb-drawer-open')]"
66
+ self.assertTrue(self.browser.is_element_present_by_xpath(nb_drawer_open_xpath, wait_time=5))
67
+ self.assertTrue(self.browser.find_by_xpath(nb_drawer_open_xpath).is_visible(wait_time=5))
68
68
  select_option = self.browser.find_by_xpath(
69
69
  ".//select[@id='id_columns']/option[contains(text(), 'Device Computed Field')]"
70
70
  )
@@ -72,6 +72,5 @@ class ComputedFieldsTestCase(SeleniumTestCase):
72
72
  select_option.click()
73
73
  self.browser.find_by_xpath(".//input[@value='Save']").click()
74
74
  self.assertTrue(self.browser.is_text_present(f"{self.device.name} is awesome!"))
75
- self.browser.find_by_xpath(".//button[@data-original-title='Configure table']").click()
76
75
  self.browser.find_by_xpath(".//input[@value='Reset']").click()
77
76
  self.assertFalse(self.browser.is_text_present(f"{self.device.name} is awesome!"))