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.
- nautobot/apps/choices.py +4 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +13 -1
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/cloud/filters.py +1 -1
- nautobot/cloud/navigation.py +3 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +6 -1
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +12 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
- nautobot/core/cli/migrate_deprecated_templates.py +227 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +62 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +35 -19
- nautobot/core/settings.yaml +17 -33
- nautobot/core/signals.py +12 -1
- nautobot/core/tables.py +13 -6
- nautobot/core/templates/40x.html +1 -1
- nautobot/core/templates/500.html +2 -2
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/admin/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/buttons/export.html +1 -1
- nautobot/core/templates/components/button/dropdown.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/panel/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +6 -7
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +22 -9
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +6 -5
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- nautobot/core/templates/generic/object_bulk_edit.html +1 -1
- nautobot/core/templates/generic/object_bulk_import.html +1 -1
- nautobot/core/templates/generic/object_bulk_remove.html +2 -2
- nautobot/core/templates/generic/object_bulk_update.html +5 -4
- nautobot/core/templates/generic/object_create.html +5 -4
- nautobot/core/templates/generic/object_delete.html +1 -1
- nautobot/core/templates/generic/object_detail.html +1 -1
- nautobot/core/templates/generic/object_edit.html +1 -1
- nautobot/core/templates/generic/object_import.html +2 -1
- nautobot/core/templates/generic/object_list.html +12 -4
- nautobot/core/templates/generic/object_notes.html +5 -3
- nautobot/core/templates/generic/object_retrieve.html +4 -5
- nautobot/core/templates/graphene/graphiql.html +7 -8
- nautobot/core/templates/home.html +1 -1
- nautobot/core/templates/import_success.html +2 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/created_updated.html +7 -3
- nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +11 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +3 -9
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -11
- nautobot/core/templates/redoc_ui.html +7 -0
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +17 -3
- nautobot/core/templates/system_jobs/import_objects.html +1 -2
- nautobot/core/templates/utilities/confirmation_form.html +2 -2
- nautobot/core/templates/utilities/obj_table.html +10 -2
- nautobot/core/templates/utilities/render_field.html +7 -7
- nautobot/core/templates/utilities/render_jinja2.html +2 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
- nautobot/core/templates/utilities/theme_preview.html +19 -3
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +76 -18
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +41 -17
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -13
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/integration/test_filters.py +48 -11
- nautobot/core/tests/integration/test_theme.py +22 -21
- nautobot/core/tests/nautobot_config.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +8 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +27 -28
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_cli.py +40 -0
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_forms.py +55 -13
- nautobot/core/tests/test_jobs.py +144 -3
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +76 -1
- nautobot/core/tests/test_patch_social_django.py +42 -0
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +62 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +123 -4
- nautobot/core/tests/test_utils.py +72 -5
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +70 -29
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +143 -27
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +143 -55
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +20 -9
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +28 -18
- nautobot/core/utils/lookup.py +49 -8
- nautobot/core/utils/module_loading.py +21 -0
- nautobot/core/utils/patch_social_django.py +128 -0
- nautobot/core/views/__init__.py +38 -1
- nautobot/core/views/generic.py +3 -3
- nautobot/core/views/mixins.py +45 -22
- nautobot/core/views/renderers.py +4 -3
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/apps.py +1 -5
- nautobot/data_validation/custom_validators.py +4 -4
- nautobot/data_validation/filters.py +1 -1
- nautobot/data_validation/forms.py +40 -0
- nautobot/data_validation/migrations/0001_initial.py +0 -7
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
- nautobot/data_validation/models.py +16 -7
- nautobot/data_validation/navigation.py +8 -1
- nautobot/data_validation/tables.py +12 -5
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +2 -2
- nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
- nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
- nautobot/data_validation/tests/test_filters.py +8 -6
- nautobot/data_validation/tests/test_models.py +15 -0
- nautobot/data_validation/tests/test_views.py +190 -32
- nautobot/data_validation/urls.py +2 -5
- nautobot/data_validation/views.py +73 -40
- nautobot/dcim/api/serializers.py +3 -13
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +65 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
- nautobot/dcim/forms.py +90 -4
- nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
- nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
- nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
- nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
- nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
- nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
- nautobot/dcim/models/device_component_templates.py +33 -1
- nautobot/dcim/models/device_components.py +98 -64
- nautobot/dcim/models/devices.py +30 -20
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +18 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +15 -15
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_trace.html +0 -4
- nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
- nautobot/dcim/templates/dcim/consoleport.html +7 -6
- nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +2 -2
- nautobot/dcim/templates/dcim/device_create.html +5 -3
- nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
- nautobot/dcim/templates/dcim/device_list.html +73 -10
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
- nautobot/dcim/templates/dcim/frontport.html +10 -9
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
- nautobot/dcim/templates/dcim/interface.html +35 -7
- nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/module_frontports.html +1 -1
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/module_powerports.html +1 -1
- nautobot/dcim/templates/dcim/module_rearports.html +1 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +6 -5
- nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
- nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
- nautobot/dcim/templates/dcim/rearport.html +8 -7
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
- nautobot/dcim/tests/integration/test_controller.py +4 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
- nautobot/dcim/tests/integration/test_create_device.py +0 -2
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
- nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
- nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
- nautobot/dcim/tests/test_api.py +194 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +55 -7
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +328 -4
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +132 -29
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +777 -397
- nautobot/extras/api/views.py +60 -45
- nautobot/extras/choices.py +2 -13
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
- nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
- nautobot/extras/{filters/__init__.py → filters.py} +33 -48
- nautobot/extras/forms/forms.py +14 -15
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +11 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +33 -14
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +60 -4
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +23 -2
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/navigation.py +13 -9
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -9
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +64 -68
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/approval_dashboard.html +7 -5
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +0 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
- nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
- nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
- nautobot/extras/templates/extras/inc/json_format.html +10 -3
- nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
- nautobot/extras/templates/extras/job.html +29 -12
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
- nautobot/extras/templates/extras/job_edit.html +52 -46
- nautobot/extras/templates/extras/job_list.html +29 -25
- nautobot/extras/templates/extras/marketplace.html +5 -9
- nautobot/extras/templates/extras/object_configcontext.html +1 -1
- nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
- nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
- nautobot/extras/templates/extras/plugin_detail.html +29 -24
- nautobot/extras/templates/extras/plugins_list.html +16 -26
- nautobot/extras/templates/extras/role_retrieve.html +64 -0
- nautobot/extras/templates/extras/scheduledjob.html +4 -2
- nautobot/extras/templates/extras/secret_create.html +1 -1
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_computedfields.py +8 -9
- nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +9 -10
- nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +90 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +59 -5
- nautobot/extras/tests/test_customfields.py +22 -13
- nautobot/extras/tests/test_customfields_filters.py +479 -0
- nautobot/extras/tests/test_dynamicgroups.py +39 -1
- nautobot/extras/tests/test_filters.py +57 -22
- nautobot/extras/tests/test_forms.py +18 -21
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +1 -0
- nautobot/extras/tests/test_models.py +51 -33
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +52 -112
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +164 -71
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +53 -38
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -2
- nautobot/ipam/signals.py +71 -0
- nautobot/ipam/tables.py +19 -6
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
- nautobot/ipam/templates/ipam/ipaddress.html +14 -0
- nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +13 -6
- nautobot/ipam/tests/test_filters.py +36 -1
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +44 -2
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +13 -14
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +53 -11
- nautobot/load_balancers/__init__.py +0 -0
- nautobot/load_balancers/api/__init__.py +1 -0
- nautobot/load_balancers/api/serializers.py +75 -0
- nautobot/load_balancers/api/urls.py +23 -0
- nautobot/load_balancers/api/views.py +61 -0
- nautobot/load_balancers/apps.py +17 -0
- nautobot/load_balancers/choices.py +167 -0
- nautobot/load_balancers/filters.py +225 -0
- nautobot/load_balancers/forms.py +532 -0
- nautobot/load_balancers/management/commands/__init__.py +0 -0
- nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
- nautobot/load_balancers/migrations/0001_initial.py +465 -0
- nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
- nautobot/load_balancers/migrations/__init__.py +0 -0
- nautobot/load_balancers/models.py +423 -0
- nautobot/load_balancers/navigation.py +80 -0
- nautobot/load_balancers/tables.py +255 -0
- nautobot/load_balancers/tests/__init__.py +474 -0
- nautobot/load_balancers/tests/test_api.py +353 -0
- nautobot/load_balancers/tests/test_filters.py +134 -0
- nautobot/load_balancers/tests/test_forms.py +266 -0
- nautobot/load_balancers/tests/test_models.py +195 -0
- nautobot/load_balancers/tests/test_views.py +229 -0
- nautobot/load_balancers/urls.py +17 -0
- nautobot/load_balancers/views.py +248 -0
- nautobot/project-static/dist/css/github-dark.min.css +10 -0
- nautobot/project-static/dist/css/github.min.css +10 -0
- nautobot/project-static/dist/css/nautobot.css +1 -11
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/forms.js +13 -0
- nautobot/project-static/js/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +1 -1
- nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
- nautobot/project-static/nautobot-icons/elements.svg +1 -1
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +1 -1
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +1 -1
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +1 -1
- nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
- nautobot/project-static/nautobot-icons/star.svg +1 -1
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +112 -30
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/users/templates/users/profile.html +45 -12
- nautobot/users/templates/users/sessionkey_delete.html +1 -1
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/views.py +4 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/models.py +1 -68
- nautobot/virtualization/navigation.py +3 -2
- nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +45 -4
- nautobot/virtualization/views.py +4 -1
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +219 -0
- nautobot/vpn/filters.py +234 -0
- nautobot/vpn/forms.py +487 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +535 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +383 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +336 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +293 -0
- nautobot/vpn/tests/test_models.py +147 -0
- nautobot/vpn/tests/test_views.py +300 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +495 -0
- nautobot/wireless/navigation.py +3 -2
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
- nautobot/circuits/templates/circuits/circuit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuit_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittermination.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittype.html +0 -2
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
- nautobot/circuits/templates/circuits/provider.html +0 -2
- nautobot/circuits/templates/circuits/provider_edit.html +0 -2
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
- nautobot/circuits/templates/circuits/provider_update.html +0 -1
- nautobot/circuits/templates/circuits/providernetwork.html +0 -2
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
- nautobot/core/templates/buttons/import.html +0 -9
- nautobot/data_validation/template_content.py +0 -42
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/dcim/templates/dcim/controller/base.html +0 -2
- nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/device/base.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
- nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
- nautobot/dcim/templates/dcim/device/frontports.html +0 -2
- nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
- nautobot/dcim/templates/dcim/device/inventory.html +0 -2
- nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
- nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
- nautobot/dcim/templates/dcim/device/powerports.html +0 -2
- nautobot/dcim/templates/dcim/device/rearports.html +0 -2
- nautobot/dcim/templates/dcim/device/wireless.html +0 -2
- nautobot/dcim/templates/dcim/device_component.html +0 -2
- nautobot/dcim/templates/dcim/device_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/devicetype.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/location.html +0 -2
- nautobot/dcim/templates/dcim/location_edit.html +0 -2
- nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
- nautobot/dcim/templates/dcim/locationtype.html +0 -2
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/manufacturer.html +0 -2
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/platform.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/rack.html +0 -2
- nautobot/dcim/templates/dcim/rack_edit.html +0 -2
- nautobot/dcim/templates/dcim/rackgroup.html +0 -2
- nautobot/dcim/templates/dcim/rackreservation.html +0 -2
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
- nautobot/dcim/ui.py +0 -29
- nautobot/extras/templates/extras/computedfield.html +0 -2
- nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontext.html +0 -2
- nautobot/extras/templates/extras/configcontext_edit.html +0 -2
- nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontextschema.html +0 -2
- nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
- nautobot/extras/templates/extras/contact_retrieve.html +0 -2
- nautobot/extras/templates/extras/customfield.html +0 -2
- nautobot/extras/templates/extras/customfield_edit.html +0 -2
- nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/customlink.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
- nautobot/extras/templates/extras/exporttemplate.html +0 -2
- nautobot/extras/templates/extras/gitrepository.html +0 -2
- nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
- nautobot/extras/templates/extras/graphqlquery.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
- nautobot/extras/templates/extras/job_detail.html +0 -2
- nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobhook.html +0 -2
- nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobresult.html +0 -2
- nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
- nautobot/extras/templates/extras/note.html +0 -2
- nautobot/extras/templates/extras/note_retrieve.html +0 -1
- nautobot/extras/templates/extras/object_changelog.html +0 -2
- nautobot/extras/templates/extras/object_notes.html +0 -2
- nautobot/extras/templates/extras/objectchange.html +0 -2
- nautobot/extras/templates/extras/objectchange_list.html +0 -3
- nautobot/extras/templates/extras/relationship.html +0 -1
- nautobot/extras/templates/extras/secret.html +0 -1
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
- nautobot/extras/templates/extras/status.html +0 -2
- nautobot/extras/templates/extras/tag.html +0 -2
- nautobot/extras/templates/extras/tag_edit.html +0 -2
- nautobot/extras/templates/extras/tag_retrieve.html +0 -2
- nautobot/extras/templates/extras/team_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/prefix.html +0 -2
- nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/rir.html +0 -2
- nautobot/ipam/templates/ipam/routetarget.html +0 -1
- nautobot/ipam/templates/ipam/service.html +0 -2
- nautobot/ipam/templates/ipam/service_edit.html +0 -2
- nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlan.html +0 -2
- nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
- nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlangroup.html +0 -2
- nautobot/ipam/templates/ipam/vrf.html +0 -1
- nautobot/tenancy/templates/tenancy/tenant.html +0 -2
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
- /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -22,7 +22,7 @@ from nautobot.core.celery import app, register_jobs
|
|
|
22
22
|
from nautobot.core.exceptions import AbortTransaction
|
|
23
23
|
from nautobot.core.jobs.bulk_actions import BulkDeleteObjects, BulkEditObjects
|
|
24
24
|
from nautobot.core.jobs.cleanup import LogsCleanup
|
|
25
|
-
from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
|
|
25
|
+
from nautobot.core.jobs.groups import RefreshDynamicGroupCacheJobButtonReceiver, RefreshDynamicGroupCaches
|
|
26
26
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
27
27
|
from nautobot.core.utils.requests import get_filterable_params_from_filter_params
|
|
28
28
|
from nautobot.data_validation import models
|
|
@@ -43,6 +43,7 @@ from nautobot.extras.jobs import (
|
|
|
43
43
|
FileVar,
|
|
44
44
|
Job,
|
|
45
45
|
MultiChoiceVar,
|
|
46
|
+
MultiObjectVar,
|
|
46
47
|
ObjectVar,
|
|
47
48
|
RunJobTaskFailed,
|
|
48
49
|
StringVar,
|
|
@@ -440,8 +441,10 @@ def clean_compliance_rules_results_for_instance(instance, excluded_pks):
|
|
|
440
441
|
class RunRegisteredDataComplianceRules(Job):
|
|
441
442
|
"""Run the validate function on all registered DataComplianceRule classes and, optionally, the built-in data validation rules."""
|
|
442
443
|
|
|
443
|
-
|
|
444
|
-
|
|
444
|
+
class Meta:
|
|
445
|
+
name = "Run Registered Data Compliance Rules"
|
|
446
|
+
description = "Runs selected Data Compliance rule classes."
|
|
447
|
+
has_sensitive_variables = False
|
|
445
448
|
|
|
446
449
|
selected_data_compliance_rules = MultiChoiceVar(
|
|
447
450
|
choices=get_data_compliance_choices,
|
|
@@ -527,6 +530,60 @@ class RunRegisteredDataComplianceRules(Job):
|
|
|
527
530
|
clean_compliance_rules_results_for_instance(instance=validated_object, excluded_pks=[result.pk])
|
|
528
531
|
|
|
529
532
|
|
|
533
|
+
class ValidateModelData(Job):
|
|
534
|
+
"""Clean and validate data in all records of a given content type(s)."""
|
|
535
|
+
|
|
536
|
+
class Meta:
|
|
537
|
+
name = "Validate Model Data"
|
|
538
|
+
description = "Run `full_clean()` against all records of a given type(s) to check for data validity."
|
|
539
|
+
has_sensitive_variables = False
|
|
540
|
+
read_only = True
|
|
541
|
+
# Validating large amounts of data may take substantial processing time
|
|
542
|
+
soft_time_limit = 1800
|
|
543
|
+
time_limit = 2000
|
|
544
|
+
|
|
545
|
+
content_types = MultiObjectVar(
|
|
546
|
+
model=ContentType,
|
|
547
|
+
description="Type(s) of objects to validate.",
|
|
548
|
+
label="Content Types",
|
|
549
|
+
query_params={"can_view": True},
|
|
550
|
+
required=True,
|
|
551
|
+
)
|
|
552
|
+
verbose = BooleanVar(default=False, label="Verbose output?")
|
|
553
|
+
|
|
554
|
+
def run(self, *, content_types, verbose=False): # pylint:disable=arguments-differ
|
|
555
|
+
for content_type in content_types:
|
|
556
|
+
model = content_type.model_class()
|
|
557
|
+
if model is None:
|
|
558
|
+
self.fail(
|
|
559
|
+
"Couldn't locate Python model for content-type %s.%s",
|
|
560
|
+
content_type.app_label,
|
|
561
|
+
content_type.model,
|
|
562
|
+
)
|
|
563
|
+
continue
|
|
564
|
+
|
|
565
|
+
try:
|
|
566
|
+
records = model.objects.restrict(self.user, "view")
|
|
567
|
+
except AttributeError: # Not a RestrictedQuerySet?
|
|
568
|
+
if self.user.is_superuser: # i.e., permissions exempt
|
|
569
|
+
records = model.objects.all()
|
|
570
|
+
else:
|
|
571
|
+
self.fail("Unable to apply access permissions to %s.%s", content_type.app_label, content_type.model)
|
|
572
|
+
|
|
573
|
+
if not records.exists():
|
|
574
|
+
self.logger.warning("No %s found", model._meta.verbose_name_plural)
|
|
575
|
+
continue
|
|
576
|
+
|
|
577
|
+
self.logger.info("Validating %d %s", records.count(), model._meta.verbose_name_plural)
|
|
578
|
+
for record in records.iterator():
|
|
579
|
+
try:
|
|
580
|
+
record.full_clean()
|
|
581
|
+
if verbose:
|
|
582
|
+
self.logger.success("Validated successfully", extra={"object": record})
|
|
583
|
+
except ValidationError as err:
|
|
584
|
+
self.fail("Validation error: `%s`", err, extra={"object": record})
|
|
585
|
+
|
|
586
|
+
|
|
530
587
|
jobs = [
|
|
531
588
|
BulkDeleteObjects,
|
|
532
589
|
BulkEditObjects,
|
|
@@ -536,6 +593,8 @@ jobs = [
|
|
|
536
593
|
ImportObjects,
|
|
537
594
|
LogsCleanup,
|
|
538
595
|
RefreshDynamicGroupCaches,
|
|
596
|
+
RefreshDynamicGroupCacheJobButtonReceiver,
|
|
539
597
|
RunRegisteredDataComplianceRules,
|
|
598
|
+
ValidateModelData,
|
|
540
599
|
]
|
|
541
600
|
register_jobs(*jobs)
|
nautobot/core/jobs/groups.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from nautobot.extras.choices import DynamicGroupTypeChoices
|
|
2
|
-
from nautobot.extras.jobs import Job, ObjectVar
|
|
2
|
+
from nautobot.extras.jobs import Job, JobButtonReceiver, ObjectVar
|
|
3
3
|
from nautobot.extras.models import DynamicGroup
|
|
4
4
|
|
|
5
5
|
name = "System Jobs"
|
|
@@ -31,8 +31,38 @@ class RefreshDynamicGroupCaches(Job):
|
|
|
31
31
|
if single_group is not None:
|
|
32
32
|
groups = groups.filter(pk=single_group.pk)
|
|
33
33
|
|
|
34
|
+
if not groups.exists():
|
|
35
|
+
self.logger.info("No relevant dynamic groups were specified, nothing to do.")
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
self.logger.info("Re-calculating and re-caching group members. This may take some time.")
|
|
34
39
|
for group in groups:
|
|
35
40
|
group.update_cached_members()
|
|
36
41
|
self.logger.info("Cache refreshed successfully, now with %d members", group.count, extra={"object": group})
|
|
37
42
|
|
|
38
43
|
self.logger.info("Cache(s) refreshed")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RefreshDynamicGroupCacheJobButtonReceiver(JobButtonReceiver):
|
|
47
|
+
"""
|
|
48
|
+
System Job Button Receiver to re-calculate and re-cache the members of a given Dynamic Group.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
class Meta:
|
|
52
|
+
name = "Refresh Dynamic Group Cache (Job Button Receiver)"
|
|
53
|
+
description = "Re-calculate and re-cache the membership list of a given Dynamic Group."
|
|
54
|
+
|
|
55
|
+
def receive_job_button(self, obj):
|
|
56
|
+
if not isinstance(obj, DynamicGroup):
|
|
57
|
+
self.fail("This job button should only be used with Dynamic Group records.")
|
|
58
|
+
elif obj.group_type == DynamicGroupTypeChoices.TYPE_STATIC:
|
|
59
|
+
self.fail(
|
|
60
|
+
"The members of this Dynamic Group are statically defined and do not need to be recalculated.",
|
|
61
|
+
extra={"object": obj},
|
|
62
|
+
)
|
|
63
|
+
else:
|
|
64
|
+
self.logger.info(
|
|
65
|
+
"Re-calculating and re-caching group members. This may take some time.", extra={"object": obj}
|
|
66
|
+
)
|
|
67
|
+
obj.update_cached_members()
|
|
68
|
+
self.logger.success("Cache refreshed successfully, now with %d members", obj.count, extra={"object": obj})
|
|
@@ -133,6 +133,14 @@ class Command(BaseCommand):
|
|
|
133
133
|
)
|
|
134
134
|
from nautobot.tenancy.factory import TenantFactory, TenantGroupFactory
|
|
135
135
|
from nautobot.users.factory import UserFactory
|
|
136
|
+
from nautobot.vpn.factory import (
|
|
137
|
+
VPNFactory,
|
|
138
|
+
VPNPhase1PolicyFactory,
|
|
139
|
+
VPNPhase2PolicyFactory,
|
|
140
|
+
VPNProfileFactory,
|
|
141
|
+
VPNTunnelEndpointFactory,
|
|
142
|
+
VPNTunnelFactory,
|
|
143
|
+
)
|
|
136
144
|
from nautobot.wireless.factory import (
|
|
137
145
|
ControllerManagedDeviceGroupWithMembersFactory,
|
|
138
146
|
RadioProfileFactory,
|
|
@@ -171,14 +179,12 @@ class Command(BaseCommand):
|
|
|
171
179
|
)
|
|
172
180
|
# ...and some tags that apply to a random subset of content-types
|
|
173
181
|
_create_batch(TagFactory, 15, description="on some content-types")
|
|
174
|
-
_create_batch(UserFactory,
|
|
182
|
+
_create_batch(UserFactory, 10)
|
|
175
183
|
_create_batch(SavedViewFactory, 20)
|
|
176
184
|
_create_batch(ContactFactory, 20)
|
|
177
185
|
_create_batch(TeamFactory, 20)
|
|
178
|
-
_create_batch(TenantGroupFactory,
|
|
179
|
-
_create_batch(
|
|
180
|
-
_create_batch(TenantFactory, 10, description="without a parent group", has_tenant_group=False)
|
|
181
|
-
_create_batch(TenantFactory, 10, description="with a parent group", has_tenant_group=True)
|
|
186
|
+
_create_batch(TenantGroupFactory, 30)
|
|
187
|
+
_create_batch(TenantFactory, 30)
|
|
182
188
|
_create_batch(LocationTypeFactory, 7) # only 7 unique LocationTypes are hard-coded presently
|
|
183
189
|
# First 7 locations must be created in specific order so subsequent objects have valid parents to reference
|
|
184
190
|
_create_batch(LocationFactory, 7, description="as structure", has_parent=True)
|
|
@@ -192,7 +198,7 @@ class Command(BaseCommand):
|
|
|
192
198
|
_create_batch(VRFFactory, 20)
|
|
193
199
|
_create_batch(VLANGroupFactory, 20)
|
|
194
200
|
_create_batch(VLANFactory, 20)
|
|
195
|
-
for i in range(
|
|
201
|
+
for i in range(50):
|
|
196
202
|
_create_batch(
|
|
197
203
|
PrefixFactory,
|
|
198
204
|
1,
|
|
@@ -207,7 +213,15 @@ class Command(BaseCommand):
|
|
|
207
213
|
prefix=f"2001:db8:0:{i}::/64",
|
|
208
214
|
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
209
215
|
)
|
|
210
|
-
_create_batch(
|
|
216
|
+
_create_batch(
|
|
217
|
+
NamespaceFactory, 5, description="with a Tenant and without any Prefixes or IPAddresses", has_tenant=True
|
|
218
|
+
)
|
|
219
|
+
_create_batch(
|
|
220
|
+
NamespaceFactory,
|
|
221
|
+
5,
|
|
222
|
+
description="without a Tenant and without any Prefixes or IPAddresses",
|
|
223
|
+
has_tenant=False,
|
|
224
|
+
)
|
|
211
225
|
_create_batch(DeviceFamilyFactory, 20)
|
|
212
226
|
_create_batch(ManufacturerFactory, 8) # First 8 hard-coded Manufacturers
|
|
213
227
|
_create_batch(PlatformFactory, 20, description="with Manufacturers", has_manufacturer=True)
|
|
@@ -222,7 +236,7 @@ class Command(BaseCommand):
|
|
|
222
236
|
_create_batch(ConsoleServerPortTemplateFactory, 30)
|
|
223
237
|
_create_batch(RearPortTemplateFactory, 30)
|
|
224
238
|
_create_batch(FrontPortTemplateFactory, 30)
|
|
225
|
-
_create_batch(InterfaceTemplateFactory,
|
|
239
|
+
_create_batch(InterfaceTemplateFactory, 50)
|
|
226
240
|
_create_batch(PowerPortTemplateFactory, 30)
|
|
227
241
|
_create_batch(PowerOutletTemplateFactory, 30)
|
|
228
242
|
_create_batch(ModuleBayTemplateFactory, 60, description="without module families", has_module_family=False)
|
|
@@ -324,11 +338,16 @@ class Command(BaseCommand):
|
|
|
324
338
|
_create_batch(WirelessNetworksWithMembersFactory, 5, description="with members")
|
|
325
339
|
# make sure we have some supported data rates that have null relationships to make filter tests happy
|
|
326
340
|
_create_batch(SupportedDataRateFactory, 10, description="without any associated objects")
|
|
341
|
+
_create_batch(VPNPhase1PolicyFactory, 20)
|
|
342
|
+
_create_batch(VPNPhase2PolicyFactory, 20)
|
|
343
|
+
_create_batch(VPNProfileFactory, 30)
|
|
344
|
+
_create_batch(VPNFactory, 10)
|
|
345
|
+
_create_batch(VPNTunnelEndpointFactory, 20)
|
|
346
|
+
_create_batch(VPNTunnelFactory, 10)
|
|
327
347
|
_create_batch(JobQueueFactory, 10)
|
|
328
348
|
# make sure we have some tenants that have null relationships to make filter tests happy
|
|
329
349
|
_create_batch(TenantFactory, 10, description="without any associated objects")
|
|
330
350
|
# TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
|
|
331
|
-
# _create_batch(UserFactory, 10)
|
|
332
351
|
# _create_batch(RackFactory, 10)
|
|
333
352
|
# _create_batch(RackReservationFactory, 10)
|
|
334
353
|
# _create_batch(ClusterTypeFactory, 10)
|
nautobot/core/models/__init__.py
CHANGED
|
@@ -102,6 +102,17 @@ class BaseModel(models.Model):
|
|
|
102
102
|
|
|
103
103
|
raise AttributeError(f"Cannot find a URL for {self} ({self._meta.app_label}.{self._meta.model_name})")
|
|
104
104
|
|
|
105
|
+
@property
|
|
106
|
+
def page_title(self):
|
|
107
|
+
"""
|
|
108
|
+
Property used by Title and Breadcrumbs to display link to the object or title at detail page.
|
|
109
|
+
"""
|
|
110
|
+
if hasattr(self, "name"):
|
|
111
|
+
return self.name
|
|
112
|
+
if hasattr(self, "display"):
|
|
113
|
+
return self.display
|
|
114
|
+
return str(self)
|
|
115
|
+
|
|
105
116
|
@property
|
|
106
117
|
def present_in_database(self):
|
|
107
118
|
"""
|
nautobot/core/models/generics.py
CHANGED
|
@@ -4,13 +4,20 @@ from nautobot.core.models import BaseModel
|
|
|
4
4
|
from nautobot.core.models.fields import TagsField
|
|
5
5
|
from nautobot.extras.models.change_logging import ChangeLoggedModel
|
|
6
6
|
from nautobot.extras.models.customfields import CustomFieldModel
|
|
7
|
-
from nautobot.extras.models.mixins import
|
|
7
|
+
from nautobot.extras.models.mixins import (
|
|
8
|
+
ContactMixin,
|
|
9
|
+
DataComplianceModelMixin,
|
|
10
|
+
DynamicGroupsModelMixin,
|
|
11
|
+
NotesMixin,
|
|
12
|
+
SavedViewMixin,
|
|
13
|
+
)
|
|
8
14
|
from nautobot.extras.models.relationships import RelationshipModel
|
|
9
15
|
|
|
10
16
|
logger = logging.getLogger(__name__)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class OrganizationalModel(
|
|
20
|
+
DataComplianceModelMixin,
|
|
14
21
|
ChangeLoggedModel,
|
|
15
22
|
ContactMixin,
|
|
16
23
|
CustomFieldModel,
|
|
@@ -35,6 +42,7 @@ class OrganizationalModel(
|
|
|
35
42
|
|
|
36
43
|
|
|
37
44
|
class PrimaryModel(
|
|
45
|
+
DataComplianceModelMixin,
|
|
38
46
|
ChangeLoggedModel,
|
|
39
47
|
ContactMixin,
|
|
40
48
|
CustomFieldModel,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
1
3
|
from django.core.cache import cache
|
|
2
4
|
from django.db.models import Case, When
|
|
3
5
|
from django.db.models.signals import post_delete, post_save
|
|
@@ -21,15 +23,18 @@ class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
|
|
|
21
23
|
Dynamically computes ancestors either through the tree or through the `parent` foreign key depending on whether
|
|
22
24
|
tree fields are present on `of`.
|
|
23
25
|
"""
|
|
26
|
+
|
|
27
|
+
# If `of` is a UUID, i.e. pk, retrieve the corresponding model instance with tree fields disabled.
|
|
28
|
+
if isinstance(of, uuid.UUID):
|
|
29
|
+
of = self.model.objects.without_tree_fields().get(pk=of)
|
|
30
|
+
|
|
24
31
|
# If `of` has `tree_depth` defined, i.e. if it was retrieved from the database on a queryset where tree fields
|
|
25
32
|
# were enabled (see `TreeQuerySet.with_tree_fields` and `TreeQuerySet.without_tree_fields`), use the default
|
|
26
33
|
# implementation from `tree_queries.query.TreeQuerySet`.
|
|
27
|
-
|
|
28
|
-
# will then annotate the tree fields and proceed as usual.
|
|
29
|
-
if hasattr(of, "tree_depth") or not hasattr(of, "parent"):
|
|
34
|
+
if hasattr(of, "tree_depth"):
|
|
30
35
|
return super().ancestors(of, include_self=include_self)
|
|
36
|
+
|
|
31
37
|
# In the other case, traverse the `parent` foreign key until the root.
|
|
32
|
-
model_class = of._meta.concrete_model
|
|
33
38
|
ancestor_pks = []
|
|
34
39
|
if include_self:
|
|
35
40
|
ancestor_pks.append(of.pk)
|
|
@@ -40,7 +45,7 @@ class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
|
|
|
40
45
|
# Reference:
|
|
41
46
|
# https://stackoverflow.com/questions/4916851/django-get-a-queryset-from-array-of-ids-in-specific-order
|
|
42
47
|
preserve_order = Case(*[When(pk=pk, then=position) for position, pk in enumerate(ancestor_pks)])
|
|
43
|
-
return
|
|
48
|
+
return self.model.objects.without_tree_fields().filter(pk__in=ancestor_pks).order_by(preserve_order)
|
|
44
49
|
|
|
45
50
|
def max_tree_depth(self):
|
|
46
51
|
r"""
|
nautobot/core/models/utils.py
CHANGED
|
@@ -147,7 +147,7 @@ def serialize_object_v2(obj):
|
|
|
147
147
|
# Try serializing obj(model instance) using its API Serializer
|
|
148
148
|
try:
|
|
149
149
|
serializer_class = get_serializer_for_model(obj.__class__)
|
|
150
|
-
data = serializer_class(obj, context={"request": None, "depth": 1}).data
|
|
150
|
+
data = serializer_class(obj, context={"request": None, "depth": 1, "exclude_m2m": False}).data
|
|
151
151
|
except SerializerNotFound:
|
|
152
152
|
# Fall back to generic JSON representation of obj
|
|
153
153
|
data = serialize_object(obj)
|
nautobot/core/settings.py
CHANGED
|
@@ -95,11 +95,12 @@ if "NAUTOBOT_DEPLOYMENT_ID" in os.environ and os.environ["NAUTOBOT_DEPLOYMENT_ID
|
|
|
95
95
|
DEPLOYMENT_ID = os.environ["NAUTOBOT_DEPLOYMENT_ID"]
|
|
96
96
|
|
|
97
97
|
# Device names are not guaranteed globally-unique by Nautobot but in practice they often are.
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
# Select how Devices are uniquely identified:
|
|
99
|
+
# - 'location_tenant_name': combination of Location + Tenant + Name
|
|
100
|
+
# - 'name': Device name must be globally unique
|
|
101
|
+
# - 'none': No enforced uniqueness (rely on other validation rules or custom validators)
|
|
102
|
+
if "NAUTOBOT_DEVICE_UNIQUENESS" in os.environ and os.environ["NAUTOBOT_DEVICE_UNIQUENESS"] != "":
|
|
103
|
+
DEVICE_UNIQUENESS = os.environ["NAUTOBOT_DEVICE_UNIQUENESS"]
|
|
103
104
|
|
|
104
105
|
# Event Brokers
|
|
105
106
|
EVENT_BROKERS = {}
|
|
@@ -412,10 +413,12 @@ SPECTACULAR_SETTINGS = {
|
|
|
412
413
|
# enum naming encountered a non-optimally resolvable collision for fields named "protocol".
|
|
413
414
|
"InterfaceRedundancyGroupProtocolChoices": "nautobot.dcim.choices.InterfaceRedundancyGroupProtocolChoices",
|
|
414
415
|
"ServiceProtocolChoices": "nautobot.ipam.choices.ServiceProtocolChoices",
|
|
416
|
+
"VirtualServerProtocolChoices": "nautobot.load_balancers.choices.ProtocolChoices",
|
|
415
417
|
# These choice enums need to be overridden because they get assigned to the `mode` field and
|
|
416
418
|
# result in this error:
|
|
417
419
|
# enum naming encountered a non-optimally resolvable collision for fields named "mode".
|
|
418
420
|
"InterfaceModeChoices": "nautobot.dcim.choices.InterfaceModeChoices",
|
|
421
|
+
"VPNPhase2PolicyChoices": "nautobot.vpn.choices.DhGroupChoices",
|
|
419
422
|
"WirelessNetworkModeChoices": "nautobot.wireless.choices.WirelessNetworkModeChoices",
|
|
420
423
|
},
|
|
421
424
|
# Create separate schema components for PATCH requests (fields generally are not `required` on PATCH)
|
|
@@ -564,11 +567,13 @@ INSTALLED_APPS = [
|
|
|
564
567
|
"nautobot.cloud",
|
|
565
568
|
"nautobot.data_validation",
|
|
566
569
|
"nautobot.dcim",
|
|
567
|
-
"nautobot.ipam",
|
|
568
570
|
"nautobot.extras",
|
|
571
|
+
"nautobot.ipam",
|
|
572
|
+
"nautobot.load_balancers",
|
|
569
573
|
"nautobot.tenancy",
|
|
570
574
|
"nautobot.users",
|
|
571
575
|
"nautobot.virtualization",
|
|
576
|
+
"nautobot.vpn",
|
|
572
577
|
"nautobot.wireless",
|
|
573
578
|
"drf_spectacular",
|
|
574
579
|
"drf_spectacular_sidecar",
|
|
@@ -759,12 +764,15 @@ CONSTANCE_CONFIG = {
|
|
|
759
764
|
help_text="Number of days to retain object changelog history.\nSet this to 0 to retain changes indefinitely.",
|
|
760
765
|
field_type=int,
|
|
761
766
|
),
|
|
762
|
-
"
|
|
763
|
-
default=
|
|
764
|
-
help_text=
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
767
|
+
"DEVICE_UNIQUENESS": ConstanceConfigItem(
|
|
768
|
+
default="location_tenant_name",
|
|
769
|
+
help_text=(
|
|
770
|
+
"Select how Devices are uniquely identified:\n"
|
|
771
|
+
"- 'location_tenant_name': combination of Location + Tenant + Name\n"
|
|
772
|
+
"- 'name': Device name must be globally unique\n"
|
|
773
|
+
"- 'none': No enforced uniqueness (rely on other validation rules or custom validators)"
|
|
774
|
+
),
|
|
775
|
+
field_type=str,
|
|
768
776
|
),
|
|
769
777
|
"DEPLOYMENT_ID": ConstanceConfigItem(
|
|
770
778
|
default="",
|
|
@@ -830,6 +838,11 @@ CONSTANCE_CONFIG = {
|
|
|
830
838
|
help_text="Whether to prefer IPv4 primary addresses over IPv6 primary addresses for devices.",
|
|
831
839
|
field_type=bool,
|
|
832
840
|
),
|
|
841
|
+
"RACK_DEFAULT_U_HEIGHT": ConstanceConfigItem(
|
|
842
|
+
default=42,
|
|
843
|
+
help_text="Default height in rack units (U) for newly created racks. Must be between 1 and 500.",
|
|
844
|
+
field_type=int,
|
|
845
|
+
),
|
|
833
846
|
"RACK_ELEVATION_DEFAULT_UNIT_HEIGHT": ConstanceConfigItem(
|
|
834
847
|
default=22, help_text="Default height (in pixels) of a rack unit in a rack elevation diagram", field_type=int
|
|
835
848
|
),
|
|
@@ -869,10 +882,11 @@ CONSTANCE_CONFIG_FIELDSETS = {
|
|
|
869
882
|
"Change Logging": ["CHANGELOG_RETENTION"],
|
|
870
883
|
"Device Connectivity": ["NETWORK_DRIVERS", "PREFER_IPV4"],
|
|
871
884
|
"Installation Metrics": ["DEPLOYMENT_ID"],
|
|
872
|
-
"Natural Keys": ["
|
|
885
|
+
"Natural Keys": ["DEVICE_UNIQUENESS", "LOCATION_NAME_AS_NATURAL_KEY"],
|
|
873
886
|
"Pagination": ["PAGINATE_COUNT", "MAX_PAGE_SIZE", "PER_PAGE_DEFAULTS"],
|
|
874
887
|
"Performance": ["JOB_CREATE_FILE_MAX_SIZE"],
|
|
875
888
|
"Rack Elevation Rendering": [
|
|
889
|
+
"RACK_DEFAULT_U_HEIGHT",
|
|
876
890
|
"RACK_ELEVATION_DEFAULT_UNIT_HEIGHT",
|
|
877
891
|
"RACK_ELEVATION_DEFAULT_UNIT_WIDTH",
|
|
878
892
|
"RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT",
|
|
@@ -1035,12 +1049,8 @@ BRANDING_FILEPATHS = {
|
|
|
1035
1049
|
"icon_mask": os.getenv(
|
|
1036
1050
|
"NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK", None
|
|
1037
1051
|
), # mono-chrome icon used for the mask-icon header
|
|
1038
|
-
"
|
|
1039
|
-
|
|
1040
|
-
), # bullet image used for various view headers
|
|
1041
|
-
"nav_bullet": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_NAV_BULLET", None), # bullet image used for nav menu headers
|
|
1042
|
-
"css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # Custom global CSS
|
|
1043
|
-
"javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # Custom global JavaScript
|
|
1052
|
+
# "css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # Custom global CSS
|
|
1053
|
+
# "javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # Custom global JavaScript
|
|
1044
1054
|
}
|
|
1045
1055
|
|
|
1046
1056
|
# Title to use in place of "Nautobot"
|
|
@@ -1147,6 +1157,12 @@ def silk_request_logging_intercept_logic(request):
|
|
|
1147
1157
|
|
|
1148
1158
|
SILKY_INTERCEPT_FUNC = silk_request_logging_intercept_logic
|
|
1149
1159
|
|
|
1160
|
+
#
|
|
1161
|
+
# django-tables2
|
|
1162
|
+
#
|
|
1163
|
+
|
|
1164
|
+
DJANGO_TABLES2_TEMPLATE = "utilities/obj_table.html"
|
|
1165
|
+
|
|
1150
1166
|
#
|
|
1151
1167
|
# Kubernetes settings variables
|
|
1152
1168
|
#
|
nautobot/core/settings.yaml
CHANGED
|
@@ -162,22 +162,17 @@ properties:
|
|
|
162
162
|
type: "string"
|
|
163
163
|
BRANDING_FILEPATHS:
|
|
164
164
|
default:
|
|
165
|
-
css: null
|
|
166
165
|
favicon: null
|
|
167
|
-
header_bullet: null
|
|
168
166
|
icon_16: null
|
|
169
167
|
icon_32: null
|
|
170
168
|
icon_180: null
|
|
171
169
|
icon_192: null
|
|
172
170
|
icon_mask: null
|
|
173
|
-
javascript: null
|
|
174
171
|
logo: null
|
|
175
|
-
nav_bullet: null
|
|
176
172
|
description: >-
|
|
177
173
|
A set of filepaths relative to the [`MEDIA_ROOT`](#media_root) which locate assets used for
|
|
178
174
|
custom branding of your Nautobot instance.
|
|
179
|
-
|
|
180
|
-
page content, each of the other assets takes the place of the corresponding stock Nautobot asset.
|
|
175
|
+
Each of the assets takes the place of the corresponding stock Nautobot asset.
|
|
181
176
|
This allows for, for instance, providing your own navbar logo and favicon.
|
|
182
177
|
If a custom asset is not provided for any of the above options, the stock Nautobot asset is used.
|
|
183
178
|
details: |-
|
|
@@ -186,22 +181,15 @@ properties:
|
|
|
186
181
|
|
|
187
182
|
+++ 2.2.4
|
|
188
183
|
The `css` and `javascript` assets were added as options.
|
|
184
|
+
|
|
185
|
+
--- 3.0.0
|
|
186
|
+
The `header_bullet`, `nav_bullet`, `javascript`, and `css` assets were removed.
|
|
189
187
|
properties:
|
|
190
|
-
css:
|
|
191
|
-
"$ref": "#/definitions/relative_path"
|
|
192
|
-
default: null
|
|
193
|
-
description: "Custom global CSS file"
|
|
194
|
-
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_CSS"
|
|
195
188
|
favicon:
|
|
196
189
|
"$ref": "#/definitions/relative_path"
|
|
197
190
|
default: null
|
|
198
191
|
description: "Browser favicon"
|
|
199
192
|
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_FAVICON"
|
|
200
|
-
header_bullet:
|
|
201
|
-
"$ref": "#/definitions/relative_path"
|
|
202
|
-
default: null
|
|
203
|
-
description: "Bullet image used for various view headers"
|
|
204
|
-
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_HEADER_BULLET"
|
|
205
193
|
icon_16:
|
|
206
194
|
"$ref": "#/definitions/relative_path"
|
|
207
195
|
default: null
|
|
@@ -227,21 +215,11 @@ properties:
|
|
|
227
215
|
default: null
|
|
228
216
|
description: "Mono-chrome icon used for the mask-icon header"
|
|
229
217
|
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK"
|
|
230
|
-
javascript:
|
|
231
|
-
"$ref": "#/definitions/relative_path"
|
|
232
|
-
default: null
|
|
233
|
-
description: "Custom global JavaScript file"
|
|
234
|
-
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT"
|
|
235
218
|
logo:
|
|
236
219
|
"$ref": "#/definitions/relative_path"
|
|
237
220
|
default: null
|
|
238
221
|
description: "Navbar logo"
|
|
239
222
|
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_LOGO"
|
|
240
|
-
nav_bullet:
|
|
241
|
-
"$ref": "#/definitions/relative_path"
|
|
242
|
-
default: null
|
|
243
|
-
description: "Bullet image used for nav menu headers"
|
|
244
|
-
environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_NAV_BULLET"
|
|
245
223
|
type: "object"
|
|
246
224
|
version_added: "1.2.0"
|
|
247
225
|
BRANDING_PREPENDED_FILENAME:
|
|
@@ -758,16 +736,22 @@ properties:
|
|
|
758
736
|
"`nautobot-server send_installation_metrics`": "../tools/nautobot-server.md#send_installation_metrics"
|
|
759
737
|
type: "string"
|
|
760
738
|
version_added: "1.6.0"
|
|
761
|
-
|
|
762
|
-
default:
|
|
739
|
+
DEVICE_UNIQUENESS:
|
|
740
|
+
default: "location_tenant_name"
|
|
763
741
|
description: >-
|
|
764
742
|
`Device` names are not guaranteed globally-unique by Nautobot but in practice they often are.
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
743
|
+
Select how Devices are uniquely identified:
|
|
744
|
+
- 'location_tenant_name': combination of Location + Tenant + Name
|
|
745
|
+
- 'name': Device name must be globally unique
|
|
746
|
+
- 'none': No enforced uniqueness (rely on other validation rules or custom validators)
|
|
747
|
+
The `DEVICE_UNIQUENESS` setting also determines which fields Nautobot treats as the natural key for a Device.
|
|
748
|
+
- If set to "location_tenant_name", the natural key is the combination of Name, Tenant, and Location.
|
|
749
|
+
- If set to "name", the natural key is the device name alone.
|
|
750
|
+
- If set to "none", Nautobot falls back to using the primary key for uniqueness.
|
|
751
|
+
environment_variable: "NAUTOBOT_DEVICE_UNIQUENESS"
|
|
768
752
|
is_constance_config: true
|
|
769
|
-
type: "
|
|
770
|
-
version_added: "
|
|
753
|
+
type: "string"
|
|
754
|
+
version_added: "3.0.0"
|
|
771
755
|
EVENT_BROKERS:
|
|
772
756
|
default: {}
|
|
773
757
|
description: >-
|
nautobot/core/signals.py
CHANGED
|
@@ -67,10 +67,21 @@ def invalidate_max_depth_cache(sender, **kwargs):
|
|
|
67
67
|
|
|
68
68
|
Note that this signal is connected in `TreeModel.__init_subclass__()` so as to only apply to those models.
|
|
69
69
|
"""
|
|
70
|
-
from nautobot.core.models.tree_queries import TreeManager
|
|
70
|
+
from nautobot.core.models.tree_queries import TreeManager, TreeNode
|
|
71
71
|
|
|
72
72
|
if not isinstance(sender.objects, TreeManager):
|
|
73
73
|
return
|
|
74
74
|
|
|
75
|
+
# If the instance is a TreeNode, and it has siblings, skip invalidating the cache.
|
|
76
|
+
instance = kwargs.get("instance", None)
|
|
77
|
+
if isinstance(instance, TreeNode):
|
|
78
|
+
try:
|
|
79
|
+
parent = getattr(instance, "parent", None)
|
|
80
|
+
except instance.DoesNotExist:
|
|
81
|
+
parent = None
|
|
82
|
+
if parent and getattr(parent, "children", None) and parent.children.count() > 1:
|
|
83
|
+
# TreeNode has siblings, depth can't change
|
|
84
|
+
return
|
|
85
|
+
|
|
75
86
|
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
76
87
|
cache.delete(sender.objects.max_depth_cache_key)
|
nautobot/core/tables.py
CHANGED
|
@@ -370,12 +370,17 @@ class ToggleColumn(django_tables2.CheckBoxColumn):
|
|
|
370
370
|
default = kwargs.pop("default", "")
|
|
371
371
|
visible = kwargs.pop("visible", False)
|
|
372
372
|
if "attrs" not in kwargs:
|
|
373
|
-
kwargs["attrs"] = {
|
|
373
|
+
kwargs["attrs"] = {
|
|
374
|
+
"input": {"class": "form-check-input nb-form-check-input-sm mt-2"},
|
|
375
|
+
"td": {"class": "nb-w-0"},
|
|
376
|
+
}
|
|
374
377
|
super().__init__(*args, default=default, visible=visible, **kwargs)
|
|
375
378
|
|
|
376
379
|
@property
|
|
377
380
|
def header(self):
|
|
378
|
-
return mark_safe(
|
|
381
|
+
return mark_safe(
|
|
382
|
+
'<input type="checkbox" class="toggle form-check-input nb-form-check-input-sm mt-2" title="Toggle all" />'
|
|
383
|
+
)
|
|
379
384
|
|
|
380
385
|
|
|
381
386
|
class BooleanColumn(django_tables2.Column):
|
|
@@ -405,7 +410,8 @@ class ButtonsColumn(django_tables2.TemplateColumn):
|
|
|
405
410
|
"th": {"class": "nb-actionable nb-w-0"},
|
|
406
411
|
}
|
|
407
412
|
# Note that braces are escaped to allow for string formatting prior to template rendering
|
|
408
|
-
template_code = """
|
|
413
|
+
template_code = """\
|
|
414
|
+
{{% if record.present_in_database %}}
|
|
409
415
|
<div class="dropdown">
|
|
410
416
|
<button class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
411
417
|
<span class="mdi mdi-dots-vertical" aria-hidden="true"></span>
|
|
@@ -439,6 +445,7 @@ class ButtonsColumn(django_tables2.TemplateColumn):
|
|
|
439
445
|
{{% endif %}}
|
|
440
446
|
</ul>
|
|
441
447
|
</div>
|
|
448
|
+
{{% endif %}}
|
|
442
449
|
"""
|
|
443
450
|
|
|
444
451
|
def __init__(
|
|
@@ -490,7 +497,7 @@ class ApprovalButtonsColumn(django_tables2.TemplateColumn):
|
|
|
490
497
|
"""
|
|
491
498
|
|
|
492
499
|
buttons = ("detail", "changelog", "approve", "deny")
|
|
493
|
-
attrs = {"td": {"class": "d-print-none text-
|
|
500
|
+
attrs = {"td": {"class": "d-print-none text-end text-nowrap"}}
|
|
494
501
|
template_name = "extras/inc/approval_buttons_column.html"
|
|
495
502
|
|
|
496
503
|
def __init__(
|
|
@@ -652,11 +659,11 @@ class LinkedCountColumn(django_tables2.Column):
|
|
|
652
659
|
{k: (getattr(record, v) or settings.FILTERS_NULL_CHOICE_VALUE) for k, v in self.url_params.items()}
|
|
653
660
|
)
|
|
654
661
|
if value > 1:
|
|
655
|
-
return format_html('<a href="{}" class="badge">{}</a>', url, value)
|
|
662
|
+
return format_html('<a href="{}" class="badge bg-primary">{}</a>', url, value)
|
|
656
663
|
if related_record is not None:
|
|
657
664
|
return helpers.hyperlinked_object(related_record, self.display_field)
|
|
658
665
|
if value == 1:
|
|
659
|
-
return format_html('<a href="{}" class="badge">{}</a>', url, value)
|
|
666
|
+
return format_html('<a href="{}" class="badge bg-primary">{}</a>', url, value)
|
|
660
667
|
return helpers.placeholder(value)
|
|
661
668
|
|
|
662
669
|
|
nautobot/core/templates/40x.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
{% block content %}
|
|
5
5
|
<div class="row justify-content-center" style="margin-top: 9.375rem;">
|
|
6
|
-
<div class="col-
|
|
6
|
+
<div class="col-sm-12 col-md-9 col-lg-7 nb-lg-max-width">
|
|
7
7
|
<div class="card">
|
|
8
8
|
<div class="card-header">
|
|
9
9
|
<strong class="hstack gap-4">{% block icon %}{% endblock %}{% block title %}{% endblock %}</strong>
|