nautobot 3.0.0a3__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 -0
- nautobot/apps/ui.py +4 -0
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/core/api/serializers.py +1 -1
- nautobot/core/api/urls.py +1 -0
- nautobot/core/api/views.py +4 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +36 -13
- nautobot/core/cli/migrate_deprecated_templates.py +36 -9
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +56 -0
- nautobot/core/management/commands/generate_test_data.py +3 -3
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +17 -7
- nautobot/core/settings.yaml +4 -26
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/tab/content_wrapper.html +4 -4
- nautobot/core/templates/echarts/echarts.html +21 -8
- nautobot/core/templates/generic/object_bulk_create.html +2 -2
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- 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_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_retrieve.html +2 -2
- nautobot/core/templates/graphene/graphiql.html +0 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/header.html +10 -0
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +1 -8
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +0 -6
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +33 -0
- nautobot/core/templates/utilities/theme_preview.html +3 -0
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templatetags/helpers.py +24 -12
- nautobot/core/testing/integration.py +24 -13
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -17
- 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/runner.py +1 -2
- nautobot/core/tests/test_breadcrumbs.py +21 -21
- nautobot/core/tests/test_jobs.py +73 -6
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_templatetags_helpers.py +9 -0
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_ui.py +122 -3
- nautobot/core/tests/test_utils.py +41 -1
- nautobot/core/ui/breadcrumbs.py +68 -17
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +49 -65
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +54 -46
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +8 -8
- nautobot/core/utils/filtering.py +11 -1
- nautobot/core/utils/lookup.py +46 -0
- nautobot/core/views/mixins.py +31 -20
- nautobot/core/views/renderers.py +2 -3
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -2
- nautobot/dcim/api/serializers.py +3 -0
- nautobot/dcim/choices.py +49 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filters.py +13 -1
- nautobot/dcim/forms.py +89 -3
- 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/{0078_remove_device_location_tenant_name_uniqueness.py → 0079_remove_device_location_tenant_name_uniqueness.py} +1 -1
- nautobot/dcim/migrations/{0079_device_name_data_migration.py → 0080_device_name_data_migration.py} +1 -1
- 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 +22 -1
- nautobot/dcim/models/devices.py +17 -4
- nautobot/dcim/tables/devices.py +15 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +1 -1
- nautobot/dcim/templates/dcim/cable_trace.html +0 -2
- nautobot/dcim/templates/dcim/consoleport.html +1 -1
- nautobot/dcim/templates/dcim/consoleserverport.html +1 -1
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/frontport.html +1 -1
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- 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 +9 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- 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_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/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +1 -1
- nautobot/dcim/templates/dcim/rack_elevation_list.html +1 -1
- nautobot/dcim/templates/dcim/rack_retrieve.html +0 -11
- nautobot/dcim/templates/dcim/rearport.html +1 -1
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_update.html +1 -1
- nautobot/dcim/tests/integration/test_controller.py +3 -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 +186 -6
- nautobot/dcim/tests/test_filters.py +43 -1
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +265 -0
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +69 -7
- nautobot/dcim/views.py +232 -126
- nautobot/extras/api/views.py +51 -44
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/filters.py +19 -2
- nautobot/extras/forms/forms.py +9 -2
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +2 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/approvals.py +11 -1
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/jobs.py +1 -3
- nautobot/extras/models/models.py +10 -2
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/views.py +0 -5
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/tables.py +12 -0
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/objectchange_retrieve.html +0 -2
- nautobot/extras/templates/extras/plugin_detail.html +3 -3
- nautobot/extras/templates/extras/secret_create.html +1 -1
- nautobot/extras/tests/integration/test_computedfields.py +8 -9
- nautobot/extras/tests/integration/test_customfields.py +1 -3
- nautobot/extras/tests/integration/test_dynamicgroups.py +7 -8
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +63 -0
- nautobot/extras/tests/test_changelog.py +24 -2
- nautobot/extras/tests/test_filters.py +36 -3
- nautobot/extras/tests/test_models.py +38 -2
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +22 -83
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +83 -52
- nautobot/ipam/filters.py +26 -0
- nautobot/ipam/tables.py +6 -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_vrfs.html +1 -1
- nautobot/ipam/tests/test_filters.py +26 -1
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/ipam/views.py +9 -7
- 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/forms.js +13 -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/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/nautobot.js +0 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/nautobot.scss +51 -2
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +1 -0
- nautobot/virtualization/views.py +4 -1
- nautobot/vpn/factory.py +25 -15
- nautobot/vpn/filters.py +1 -0
- nautobot/vpn/forms.py +1 -0
- nautobot/vpn/migrations/0001_initial.py +1 -1
- nautobot/vpn/models.py +16 -8
- nautobot/vpn/tables.py +5 -2
- nautobot/vpn/tests/test_api.py +0 -5
- nautobot/vpn/tests/test_forms.py +1 -2
- nautobot/vpn/tests/test_models.py +57 -7
- nautobot/vpn/tests/test_views.py +22 -3
- nautobot/vpn/views.py +78 -20
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +4 -4
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +243 -352
- 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/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/cable.html +0 -2
- nautobot/dcim/templates/dcim/cable_edit.html +0 -2
- 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 -2
- 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 -2
- 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/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/vpn/templates/vpn/vpnprofile.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-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +0 -0
nautobot/apps/choices.py
CHANGED
|
@@ -14,8 +14,10 @@ from nautobot.dcim.choices import (
|
|
|
14
14
|
ConsolePortTypeChoices,
|
|
15
15
|
DeviceFaceChoices,
|
|
16
16
|
DeviceRedundancyGroupFailoverStrategyChoices,
|
|
17
|
+
InterfaceDuplexChoices,
|
|
17
18
|
InterfaceModeChoices,
|
|
18
19
|
InterfaceRedundancyGroupProtocolChoices,
|
|
20
|
+
InterfaceSpeedChoices,
|
|
19
21
|
InterfaceTypeChoices,
|
|
20
22
|
PortTypeChoices,
|
|
21
23
|
PowerFeedBreakerPoleChoices,
|
|
@@ -84,8 +86,10 @@ __all__ = (
|
|
|
84
86
|
"IPAddressRoleChoices",
|
|
85
87
|
"IPAddressTypeChoices",
|
|
86
88
|
"IPAddressVersionChoices",
|
|
89
|
+
"InterfaceDuplexChoices",
|
|
87
90
|
"InterfaceModeChoices",
|
|
88
91
|
"InterfaceRedundancyGroupProtocolChoices",
|
|
92
|
+
"InterfaceSpeedChoices",
|
|
89
93
|
"InterfaceTypeChoices",
|
|
90
94
|
"JobExecutionType",
|
|
91
95
|
"JobResultStatusChoices",
|
nautobot/apps/ui.py
CHANGED
|
@@ -4,6 +4,7 @@ from nautobot.core.choices import ButtonColorChoices
|
|
|
4
4
|
from nautobot.core.ui.base import PermissionsMixin
|
|
5
5
|
from nautobot.core.ui.breadcrumbs import (
|
|
6
6
|
AncestorsBreadcrumbs,
|
|
7
|
+
AncestorsInstanceBreadcrumbItem,
|
|
7
8
|
BaseBreadcrumbItem,
|
|
8
9
|
Breadcrumbs,
|
|
9
10
|
context_object_attr,
|
|
@@ -13,6 +14,7 @@ from nautobot.core.ui.breadcrumbs import (
|
|
|
13
14
|
ViewNameBreadcrumbItem,
|
|
14
15
|
)
|
|
15
16
|
from nautobot.core.ui.choices import (
|
|
17
|
+
EChartsThemeColors,
|
|
16
18
|
EChartsTypeChoices,
|
|
17
19
|
LayoutChoices,
|
|
18
20
|
NavigationIconChoices,
|
|
@@ -65,6 +67,7 @@ from nautobot.extras.plugins import Banner, TemplateExtension
|
|
|
65
67
|
|
|
66
68
|
__all__ = (
|
|
67
69
|
"AncestorsBreadcrumbs",
|
|
70
|
+
"AncestorsInstanceBreadcrumbItem",
|
|
68
71
|
"Banner",
|
|
69
72
|
"BannerClassChoices",
|
|
70
73
|
"BaseBreadcrumbItem",
|
|
@@ -78,6 +81,7 @@ __all__ = (
|
|
|
78
81
|
"DropdownButton",
|
|
79
82
|
"EChartsBase",
|
|
80
83
|
"EChartsPanel",
|
|
84
|
+
"EChartsThemeColors",
|
|
81
85
|
"EChartsTypeChoices",
|
|
82
86
|
"GroupedKeyValueTablePanel",
|
|
83
87
|
"HomePageBase",
|
nautobot/apps/utils.py
CHANGED
|
@@ -27,10 +27,13 @@ from nautobot.core.utils.filtering import (
|
|
|
27
27
|
from nautobot.core.utils.git import BranchDoesNotExist, convert_git_diff_log_to_list, GitRepo, swap_status_initials
|
|
28
28
|
from nautobot.core.utils.logging import sanitize
|
|
29
29
|
from nautobot.core.utils.lookup import (
|
|
30
|
+
get_breadcrumbs_for_model,
|
|
30
31
|
get_changes_for_model,
|
|
32
|
+
get_detail_view_components_context_for_model,
|
|
31
33
|
get_filterset_for_model,
|
|
32
34
|
get_form_for_model,
|
|
33
35
|
get_model_from_name,
|
|
36
|
+
get_object_detail_content_for_model,
|
|
34
37
|
get_related_class_for_model,
|
|
35
38
|
get_related_field_for_models,
|
|
36
39
|
get_route_for_model,
|
|
@@ -38,6 +41,7 @@ from nautobot.core.utils.lookup import (
|
|
|
38
41
|
get_url_for_url_pattern,
|
|
39
42
|
get_url_patterns,
|
|
40
43
|
get_view_for_model,
|
|
44
|
+
get_view_titles_for_model,
|
|
41
45
|
)
|
|
42
46
|
from nautobot.core.utils.migrations import migrate_content_type_references_to_new_model
|
|
43
47
|
from nautobot.core.utils.permissions import (
|
|
@@ -97,8 +101,10 @@ __all__ = (
|
|
|
97
101
|
"generate_signature",
|
|
98
102
|
"get_all_lookup_expr_for_field",
|
|
99
103
|
"get_base_template",
|
|
104
|
+
"get_breadcrumbs_for_model",
|
|
100
105
|
"get_celery_queues",
|
|
101
106
|
"get_changes_for_model",
|
|
107
|
+
"get_detail_view_components_context_for_model",
|
|
102
108
|
"get_filter_field_label",
|
|
103
109
|
"get_filterable_params_from_filter_params",
|
|
104
110
|
"get_filterset_field",
|
|
@@ -107,6 +113,7 @@ __all__ = (
|
|
|
107
113
|
"get_form_for_model",
|
|
108
114
|
"get_latest_release",
|
|
109
115
|
"get_model_from_name",
|
|
116
|
+
"get_object_detail_content_for_model",
|
|
110
117
|
"get_permission_for_model",
|
|
111
118
|
"get_related_class_for_model",
|
|
112
119
|
"get_related_field_for_models",
|
|
@@ -116,6 +123,7 @@ __all__ = (
|
|
|
116
123
|
"get_url_for_url_pattern",
|
|
117
124
|
"get_url_patterns",
|
|
118
125
|
"get_view_for_model",
|
|
126
|
+
"get_view_titles_for_model",
|
|
119
127
|
"get_worker_count",
|
|
120
128
|
"hex_to_rgb",
|
|
121
129
|
"image_upload",
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
|
|
3
|
-
from django.test import tag
|
|
4
|
-
|
|
5
3
|
from nautobot.circuits.models import Circuit, CircuitType, Provider
|
|
6
4
|
from nautobot.core.testing.integration import (
|
|
7
5
|
BulkOperationsTestCases,
|
|
@@ -9,7 +7,6 @@ from nautobot.core.testing.integration import (
|
|
|
9
7
|
from nautobot.extras.models import Status
|
|
10
8
|
|
|
11
9
|
|
|
12
|
-
@tag("fix_in_v3")
|
|
13
10
|
class CircuitBulkOperationsTestCase(BulkOperationsTestCases.BulkOperationsTestCase):
|
|
14
11
|
"""
|
|
15
12
|
Test circuits bulk edit / delete operations.
|
nautobot/circuits/views.py
CHANGED
|
@@ -60,7 +60,7 @@ class CircuitTypeUIViewSet(NautobotUIViewSet):
|
|
|
60
60
|
|
|
61
61
|
class CircuitTerminationObjectFieldsPanel(ObjectFieldsPanel):
|
|
62
62
|
def get_extra_context(self, context):
|
|
63
|
-
return {"termination": context["object"]}
|
|
63
|
+
return {"termination": context["object"], **super().get_extra_context(context)}
|
|
64
64
|
|
|
65
65
|
def render_key(self, key, value, context):
|
|
66
66
|
if key == "connected_endpoint":
|
|
@@ -226,7 +226,11 @@ class CircuitUIViewSet(NautobotUIViewSet):
|
|
|
226
226
|
return True
|
|
227
227
|
|
|
228
228
|
def get_extra_context(self, context):
|
|
229
|
-
return {
|
|
229
|
+
return {
|
|
230
|
+
"termination": context[self.context_object_key],
|
|
231
|
+
"side": self.side,
|
|
232
|
+
**super().get_extra_context(context),
|
|
233
|
+
}
|
|
230
234
|
|
|
231
235
|
def get_data(self, context):
|
|
232
236
|
"""
|
nautobot/core/api/serializers.py
CHANGED
|
@@ -104,7 +104,7 @@ class OptInFieldsMixin:
|
|
|
104
104
|
# If exclude_m2m is present and truthy, mark any many-to-many fields as write-only so they
|
|
105
105
|
# don't get included in the response.
|
|
106
106
|
# If exclude_m2m is not present, we include a subset of many-to-many fields by default.
|
|
107
|
-
exclude_m2m = params.get("exclude_m2m")
|
|
107
|
+
exclude_m2m = params.get("exclude_m2m", self.context.get("exclude_m2m", None))
|
|
108
108
|
if exclude_m2m is None or is_truthy(exclude_m2m):
|
|
109
109
|
for field_instance in fields.values():
|
|
110
110
|
if isinstance(field_instance, (serializers.ManyRelatedField, serializers.ListSerializer)):
|
nautobot/core/api/urls.py
CHANGED
|
@@ -48,6 +48,7 @@ urlpatterns = [
|
|
|
48
48
|
path("dcim/", include("nautobot.dcim.api.urls")),
|
|
49
49
|
path("extras/", include("nautobot.extras.api.urls")),
|
|
50
50
|
path("ipam/", include("nautobot.ipam.api.urls")),
|
|
51
|
+
path("load-balancers/", include("nautobot.load_balancers.api.urls")),
|
|
51
52
|
path("tenancy/", include("nautobot.tenancy.api.urls")),
|
|
52
53
|
path("users/", include("nautobot.users.api.urls")),
|
|
53
54
|
path("virtualization/", include("nautobot.virtualization.api.urls")),
|
nautobot/core/api/views.py
CHANGED
|
@@ -458,6 +458,10 @@ class APIRootView(AuthenticatedAPIRootView):
|
|
|
458
458
|
"ipam",
|
|
459
459
|
reverse("ipam-api:api-root", request=request, format=format),
|
|
460
460
|
),
|
|
461
|
+
(
|
|
462
|
+
"load-balancers",
|
|
463
|
+
reverse("load_balancers-api:api-root", request=request, format=format),
|
|
464
|
+
),
|
|
461
465
|
(
|
|
462
466
|
"plugins",
|
|
463
467
|
reverse("plugins-api:api-root", request=request, format=format),
|
nautobot/core/choices.py
CHANGED
|
@@ -3,6 +3,8 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
|
|
6
|
+
from .migrate_deprecated_templates import replace_deprecated_templates
|
|
7
|
+
|
|
6
8
|
logger = logging.getLogger(__name__)
|
|
7
9
|
|
|
8
10
|
|
|
@@ -647,7 +649,7 @@ def convert_bootstrap_classes(html_input: str, file_path: str) -> tuple[str, dic
|
|
|
647
649
|
# --- File Processing ---
|
|
648
650
|
|
|
649
651
|
|
|
650
|
-
def fix_html_files_in_directory(directory: str, resize=False) -> None:
|
|
652
|
+
def fix_html_files_in_directory(directory: str, resize=False, dry_run=False, skip_templates=False) -> None:
|
|
651
653
|
"""
|
|
652
654
|
Recursively finds all .html files in the given directory, applies convert_bootstrap_classes,
|
|
653
655
|
and overwrites each file with the fixed content. If resize is True, it will only change the
|
|
@@ -669,6 +671,9 @@ def fix_html_files_in_directory(directory: str, resize=False) -> None:
|
|
|
669
671
|
# Breakpoints that are not xs do not count as failures in djlint, so we keep a separate counter
|
|
670
672
|
resizing_other = 0
|
|
671
673
|
|
|
674
|
+
if not os.path.exists(directory):
|
|
675
|
+
raise FileNotFoundError(directory)
|
|
676
|
+
|
|
672
677
|
if os.path.isfile(directory):
|
|
673
678
|
only_filename = os.path.basename(directory)
|
|
674
679
|
directory = os.path.dirname(directory)
|
|
@@ -715,9 +720,12 @@ def fix_html_files_in_directory(directory: str, resize=False) -> None:
|
|
|
715
720
|
|
|
716
721
|
fixed_content, stats = convert_bootstrap_classes(content, file_path=file_path)
|
|
717
722
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
723
|
+
if dry_run:
|
|
724
|
+
logger.info("Would fix: %s", file_path)
|
|
725
|
+
else:
|
|
726
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
727
|
+
f.write(fixed_content)
|
|
728
|
+
logger.info("Fixed: %s", file_path)
|
|
721
729
|
|
|
722
730
|
if any(stats.values()):
|
|
723
731
|
print(f"→ {os.path.relpath(file_path, directory)}: ", end="")
|
|
@@ -743,19 +751,23 @@ def fix_html_files_in_directory(directory: str, resize=False) -> None:
|
|
|
743
751
|
if k in totals:
|
|
744
752
|
totals[k] += v
|
|
745
753
|
|
|
754
|
+
templates_replaced = replace_deprecated_templates(directory, dry_run=dry_run) if not skip_templates else 0
|
|
755
|
+
|
|
746
756
|
# Global summary
|
|
747
757
|
total_issues = sum(totals.values())
|
|
748
758
|
print("=== Global Summary ===")
|
|
749
759
|
print(f"Total issues fixed: {total_issues}")
|
|
750
|
-
print(f"- Class replacements:
|
|
751
|
-
print(f"- Extra-breadcrumb fixes:
|
|
752
|
-
print(f"- <li> in <ol.breadcrumb>:
|
|
753
|
-
print(f"- <li> in <ul.nav-tabs>:
|
|
754
|
-
print(f"- <a> in <ul.dropdown-menu>:
|
|
755
|
-
print(f"- Panel class replacements:
|
|
756
|
-
print(f"- Resizing breakpoint xs:
|
|
760
|
+
print(f"- Class replacements: {totals['replacements']}")
|
|
761
|
+
print(f"- Extra-breadcrumb fixes: {totals['extra_breadcrumbs']}")
|
|
762
|
+
print(f"- <li> in <ol.breadcrumb>: {totals['breadcrumb_items']}")
|
|
763
|
+
print(f"- <li> in <ul.nav-tabs>: {totals['nav_items']}")
|
|
764
|
+
print(f"- <a> in <ul.dropdown-menu>: {totals['dropdown_items']}")
|
|
765
|
+
print(f"- Panel class replacements: {totals['panel_classes']}")
|
|
766
|
+
print(f"- Resizing breakpoint xs: {totals['resizing_xs']}")
|
|
767
|
+
print("-------------------------------------")
|
|
768
|
+
print(f"- Resizing other breakpoints: {resizing_other}")
|
|
757
769
|
print("-------------------------------------")
|
|
758
|
-
print(f"-
|
|
770
|
+
print(f"- Deprecated templates replaced: {templates_replaced}")
|
|
759
771
|
|
|
760
772
|
|
|
761
773
|
def main():
|
|
@@ -766,10 +778,21 @@ def main():
|
|
|
766
778
|
action="store_true",
|
|
767
779
|
help="Change column breakpoints to be one level higher, such as 'col-xs-*' to 'col-sm-*'",
|
|
768
780
|
)
|
|
781
|
+
parser.add_argument(
|
|
782
|
+
"-d",
|
|
783
|
+
"--dry-run",
|
|
784
|
+
action="store_true",
|
|
785
|
+
help="Show which files would be modified without making any changes.",
|
|
786
|
+
)
|
|
769
787
|
parser.add_argument("path", type=str, help="Path to directory in which to recursively fix all .html files.")
|
|
788
|
+
parser.add_argument(
|
|
789
|
+
"-st", "--skip-template-replacement", action="store_true", help="Skip replacing deprecated templates."
|
|
790
|
+
)
|
|
770
791
|
args = parser.parse_args()
|
|
771
792
|
|
|
772
|
-
fix_html_files_in_directory(
|
|
793
|
+
fix_html_files_in_directory(
|
|
794
|
+
args.path, resize=args.resize, dry_run=args.dry_run, skip_templates=args.skip_template_replacement
|
|
795
|
+
)
|
|
773
796
|
|
|
774
797
|
|
|
775
798
|
if __name__ == "__main__":
|
|
@@ -4,15 +4,16 @@ import re
|
|
|
4
4
|
|
|
5
5
|
TEMPLATE_REPLACEMENTS = {
|
|
6
6
|
# Format: new_template: [old_template1, old_template2, ...]
|
|
7
|
-
"circuits/circuit_create.html": ["circuits/circuit_edit.html"],
|
|
8
|
-
"circuits/circuittermination_create.html": [
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
"circuits/circuit_create.html": ["circuits/circuit_edit.html", "circuits/circuit_update.html"],
|
|
8
|
+
"circuits/circuittermination_create.html": [
|
|
9
|
+
"circuits/circuittermination_edit.html",
|
|
10
|
+
"circuits/circuittermination_update.html",
|
|
11
|
+
],
|
|
12
|
+
"circuits/provider_create.html": ["circuits/provider_edit.html", "circuits/provider_update.html"],
|
|
11
13
|
"dcim/cable_retrieve.html": ["dcim/cable.html"],
|
|
12
14
|
"dcim/cable_update.html": ["dcim/cable_edit.html"],
|
|
13
15
|
"dcim/device_create.html": ["dcim/device_edit.html"],
|
|
14
16
|
"dcim/devicetype_update.html": ["dcim/devicetype_edit.html"],
|
|
15
|
-
"dcim/location_retrieve.html": ["dcim/location.html"],
|
|
16
17
|
"dcim/location_update.html": ["dcim/location_edit.html"],
|
|
17
18
|
"dcim/rack_retrieve.html": ["dcim/rack.html"],
|
|
18
19
|
"dcim/rack_update.html": ["dcim/rack_edit.html"],
|
|
@@ -26,10 +27,9 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
26
27
|
"extras/dynamicgroup_update.html": ["extras/dynamicgroup_edit.html"],
|
|
27
28
|
"extras/gitrepository_retrieve.html": ["extras/gitrepository.html"],
|
|
28
29
|
"extras/gitrepository_update.html": ["extras/gitrepository_object_edit.html"],
|
|
29
|
-
"extras/graphqlquery_retrieve.html": ["extras/graphqlquery.html"],
|
|
30
30
|
"extras/jobresult_retrieve.html": ["extras/jobresult.html"],
|
|
31
|
-
"extras/note_retrieve.html": ["extras/note.html"],
|
|
32
31
|
"extras/objectchange_retrieve.html": ["extras/objectchange.html"],
|
|
32
|
+
"extras/secret_create.html": ["extras/secret_edit.html"],
|
|
33
33
|
"extras/secretsgroup_update.html": ["extras/secretsgroup_edit.html"],
|
|
34
34
|
"extras/tag_update.html": ["extras/tag_edit.html"],
|
|
35
35
|
"generic/object_bulk_create.html": ["generic/object_bulk_import.html"],
|
|
@@ -38,6 +38,7 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
38
38
|
"generic/object_changelog.html": ["extras/object_changelog.html"],
|
|
39
39
|
"generic/object_create.html": ["dcim/powerpanel_edit.html", "generic/object_edit.html", "ipam/service_edit.html"],
|
|
40
40
|
"generic/object_destroy.html": ["generic/object_delete.html"],
|
|
41
|
+
"generic/object_list.html": ["extras/graphqlquery_list.html", "extras/objectchange_list.html"],
|
|
41
42
|
"generic/object_notes.html": ["extras/object_notes.html"],
|
|
42
43
|
"generic/object_retrieve.html": [
|
|
43
44
|
"circuits/circuit.html",
|
|
@@ -46,6 +47,8 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
46
47
|
"circuits/circuittermination_retrieve.html",
|
|
47
48
|
"circuits/circuittype.html",
|
|
48
49
|
"circuits/circuittype_retrieve.html",
|
|
50
|
+
"circuits/provider.html",
|
|
51
|
+
"circuits/provider_retrieve.html",
|
|
49
52
|
"circuits/providernetwork.html",
|
|
50
53
|
"circuits/providernetwork_retrieve.html",
|
|
51
54
|
"cloud/cloudaccount_retrieve.html",
|
|
@@ -68,14 +71,18 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
68
71
|
"dcim/device/powerports.html",
|
|
69
72
|
"dcim/device/rearports.html",
|
|
70
73
|
"dcim/device/wireless.html",
|
|
74
|
+
"dcim/device_component.html",
|
|
71
75
|
"dcim/devicefamily_retrieve.html",
|
|
72
76
|
"dcim/deviceredundancygroup_retrieve.html",
|
|
73
77
|
"dcim/devicetype.html",
|
|
74
78
|
"dcim/devicetype_retrieve.html",
|
|
75
79
|
"dcim/interfaceredundancygroup_retrieve.html",
|
|
80
|
+
"dcim/location.html",
|
|
81
|
+
"dcim/location_retrieve.html",
|
|
76
82
|
"dcim/locationtype.html",
|
|
77
83
|
"dcim/locationtype_retrieve.html",
|
|
78
84
|
"dcim/manufacturer.html",
|
|
85
|
+
"dcim/modulebay_retrieve.html",
|
|
79
86
|
"dcim/platform.html",
|
|
80
87
|
"dcim/powerfeed.html",
|
|
81
88
|
"dcim/powerfeed_retrieve.html",
|
|
@@ -96,11 +103,17 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
96
103
|
"extras/customfield_retrieve.html",
|
|
97
104
|
"extras/customlink.html",
|
|
98
105
|
"extras/exporttemplate.html",
|
|
106
|
+
"extras/graphqlquery.html",
|
|
107
|
+
"extras/graphqlquery_retrieve.html",
|
|
99
108
|
"extras/job_detail.html",
|
|
100
109
|
"extras/jobbutton_retrieve.html",
|
|
101
110
|
"extras/jobhook.html",
|
|
102
111
|
"extras/jobqueue_retrieve.html",
|
|
103
112
|
"extras/metadatatype_retrieve.html",
|
|
113
|
+
"extras/note.html",
|
|
114
|
+
"extras/note_retrieve.html",
|
|
115
|
+
"extras/relationship.html",
|
|
116
|
+
"extras/secret.html",
|
|
104
117
|
"extras/secretsgroup.html",
|
|
105
118
|
"extras/secretsgroup_retrieve.html",
|
|
106
119
|
"extras/status.html",
|
|
@@ -108,13 +121,20 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
108
121
|
"extras/tag_retrieve.html",
|
|
109
122
|
"extras/team_retrieve.html",
|
|
110
123
|
"generic/object_detail.html",
|
|
124
|
+
"ipam/namespace_retrieve.html",
|
|
125
|
+
"ipam/prefix.html",
|
|
126
|
+
"ipam/prefix_retrieve.html",
|
|
111
127
|
"ipam/rir.html",
|
|
128
|
+
"ipam/routetarget.html",
|
|
112
129
|
"ipam/service.html",
|
|
113
130
|
"ipam/service_retrieve.html",
|
|
114
131
|
"ipam/vlan.html",
|
|
115
132
|
"ipam/vlan_retrieve.html",
|
|
116
133
|
"ipam/vlangroup.html",
|
|
134
|
+
"ipam/vrf.html",
|
|
117
135
|
"tenancy/tenant.html",
|
|
136
|
+
"tenancy/tenantgroup.html",
|
|
137
|
+
"tenancy/tenantgroup_retrieve.html",
|
|
118
138
|
"virtualization/clustergroup.html",
|
|
119
139
|
"virtualization/clustertype.html",
|
|
120
140
|
"virtualization/virtualmachine.html",
|
|
@@ -123,10 +143,9 @@ TEMPLATE_REPLACEMENTS = {
|
|
|
123
143
|
"wireless/supporteddatarate_retrieve.html",
|
|
124
144
|
"wireless/wirelessnetwork_retrieve.html",
|
|
125
145
|
],
|
|
126
|
-
"ipam/
|
|
146
|
+
"ipam/prefix_create.html": ["ipam/prefix_edit.html"],
|
|
127
147
|
"ipam/vlan_update.html": ["ipam/vlan_edit.html"],
|
|
128
148
|
"tenancy/tenant_create.html": ["tenancy/tenant_edit.html"],
|
|
129
|
-
"tenancy/tenantgroup_retrieve.html": ["tenancy/tenantgroup.html"],
|
|
130
149
|
"virtualchassis_update.html": ["dcim/virtualchassis_edit.html"],
|
|
131
150
|
"virtualization/virtualmachine_update.html": ["virtualization/virtualmachine_edit.html"],
|
|
132
151
|
}
|
|
@@ -164,6 +183,8 @@ def replace_deprecated_templates(path: str, dry_run: bool = False):
|
|
|
164
183
|
path = os.path.dirname(path)
|
|
165
184
|
else:
|
|
166
185
|
only_filename = None
|
|
186
|
+
print("Finding deprecated templates to replace...")
|
|
187
|
+
count = 0
|
|
167
188
|
|
|
168
189
|
for root, _, files in os.walk(path):
|
|
169
190
|
for filename in files:
|
|
@@ -179,12 +200,18 @@ def replace_deprecated_templates(path: str, dry_run: bool = False):
|
|
|
179
200
|
fixed_content, was_updated = replace_template_references(content)
|
|
180
201
|
|
|
181
202
|
if was_updated:
|
|
203
|
+
count += 1
|
|
182
204
|
if dry_run:
|
|
183
205
|
print(f"Detected deprecated template reference in {file_path}")
|
|
184
206
|
continue
|
|
185
207
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
186
208
|
f.write(fixed_content)
|
|
187
209
|
print(f"Updated: {file_path}")
|
|
210
|
+
if not count:
|
|
211
|
+
print("No deprecated templates found.")
|
|
212
|
+
else:
|
|
213
|
+
print(f"Found {count} deprecated templates.")
|
|
214
|
+
return count
|
|
188
215
|
|
|
189
216
|
|
|
190
217
|
def main():
|
nautobot/core/filters.py
CHANGED
|
@@ -101,6 +101,10 @@ class MultiValueDateTimeFilter(django_filters.DateTimeFilter, django_filters.Mul
|
|
|
101
101
|
class MultiValueNumberFilter(django_filters.NumberFilter, django_filters.MultipleChoiceFilter):
|
|
102
102
|
field_class = multivalue_field_factory(django_forms.IntegerField)
|
|
103
103
|
|
|
104
|
+
def __init__(self, *args, choices=None, **kwargs):
|
|
105
|
+
super().__init__(*args, **kwargs)
|
|
106
|
+
self.choices = list(choices) if choices is not None else None
|
|
107
|
+
|
|
104
108
|
|
|
105
109
|
class MultiValueBigNumberFilter(MultiValueNumberFilter):
|
|
106
110
|
"""Subclass of MultiValueNumberFilter used for BigInteger model fields."""
|
nautobot/core/forms/__init__.py
CHANGED
|
@@ -69,6 +69,7 @@ from nautobot.core.forms.widgets import (
|
|
|
69
69
|
DatePicker,
|
|
70
70
|
DateTimePicker,
|
|
71
71
|
MultiValueCharInput,
|
|
72
|
+
NumberWithSelect,
|
|
72
73
|
SelectWithDisabled,
|
|
73
74
|
SelectWithPK,
|
|
74
75
|
SlugWidget,
|
|
@@ -126,6 +127,7 @@ __all__ = (
|
|
|
126
127
|
"MultiValueCharInput",
|
|
127
128
|
"MultipleContentTypeField",
|
|
128
129
|
"NullableDateField",
|
|
130
|
+
"NumberWithSelect",
|
|
129
131
|
"NumericArrayField",
|
|
130
132
|
"PrefixFieldMixin",
|
|
131
133
|
"ReturnURLForm",
|
nautobot/core/forms/widgets.py
CHANGED
|
@@ -6,7 +6,7 @@ from django import forms
|
|
|
6
6
|
from django.forms.models import ModelChoiceIterator
|
|
7
7
|
from django.urls import get_script_prefix
|
|
8
8
|
|
|
9
|
-
from nautobot.core import choices
|
|
9
|
+
from nautobot.core import choices as core_choices
|
|
10
10
|
from nautobot.core.forms import utils
|
|
11
11
|
|
|
12
12
|
__all__ = (
|
|
@@ -19,6 +19,7 @@ __all__ = (
|
|
|
19
19
|
"ContentTypeSelect",
|
|
20
20
|
"DatePicker",
|
|
21
21
|
"DateTimePicker",
|
|
22
|
+
"NumberWithSelect",
|
|
22
23
|
"SelectWithDisabled",
|
|
23
24
|
"SelectWithPK",
|
|
24
25
|
"SlugWidget",
|
|
@@ -68,7 +69,7 @@ class ColorSelect(forms.Select):
|
|
|
68
69
|
option_template_name = "widgets/colorselect_option.html"
|
|
69
70
|
|
|
70
71
|
def __init__(self, *args, **kwargs):
|
|
71
|
-
kwargs["choices"] = utils.add_blank_choice(
|
|
72
|
+
kwargs["choices"] = utils.add_blank_choice(core_choices.ColorChoices)
|
|
72
73
|
super().__init__(*args, **kwargs)
|
|
73
74
|
self.attrs["class"] = "nautobot-select2-color-picker"
|
|
74
75
|
|
|
@@ -283,3 +284,21 @@ class ClearableFileInput(forms.ClearableFileInput):
|
|
|
283
284
|
|
|
284
285
|
class Media:
|
|
285
286
|
js = ["bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js"]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class NumberWithSelect(forms.NumberInput):
|
|
290
|
+
template_name = "widgets/number_input_with_choices.html"
|
|
291
|
+
|
|
292
|
+
def __init__(self, choices=None, *args, **kwargs):
|
|
293
|
+
super().__init__(*args, **kwargs)
|
|
294
|
+
if choices is None:
|
|
295
|
+
self.choices = []
|
|
296
|
+
elif hasattr(choices, "CHOICES"):
|
|
297
|
+
self.choices = core_choices.unpack_grouped_choices(choices.CHOICES)
|
|
298
|
+
else:
|
|
299
|
+
self.choices = core_choices.unpack_grouped_choices(choices)
|
|
300
|
+
|
|
301
|
+
def get_context(self, name, value, attrs):
|
|
302
|
+
context = super().get_context(name, value, attrs)
|
|
303
|
+
context["widget"]["choices"] = self.choices
|
|
304
|
+
return context
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -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,
|
|
@@ -529,6 +530,60 @@ class RunRegisteredDataComplianceRules(Job):
|
|
|
529
530
|
clean_compliance_rules_results_for_instance(instance=validated_object, excluded_pks=[result.pk])
|
|
530
531
|
|
|
531
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
|
+
|
|
532
587
|
jobs = [
|
|
533
588
|
BulkDeleteObjects,
|
|
534
589
|
BulkEditObjects,
|
|
@@ -540,5 +595,6 @@ jobs = [
|
|
|
540
595
|
RefreshDynamicGroupCaches,
|
|
541
596
|
RefreshDynamicGroupCacheJobButtonReceiver,
|
|
542
597
|
RunRegisteredDataComplianceRules,
|
|
598
|
+
ValidateModelData,
|
|
543
599
|
]
|
|
544
600
|
register_jobs(*jobs)
|
|
@@ -338,9 +338,9 @@ class Command(BaseCommand):
|
|
|
338
338
|
_create_batch(WirelessNetworksWithMembersFactory, 5, description="with members")
|
|
339
339
|
# make sure we have some supported data rates that have null relationships to make filter tests happy
|
|
340
340
|
_create_batch(SupportedDataRateFactory, 10, description="without any associated objects")
|
|
341
|
-
_create_batch(VPNPhase1PolicyFactory,
|
|
342
|
-
_create_batch(VPNPhase2PolicyFactory,
|
|
343
|
-
_create_batch(VPNProfileFactory,
|
|
341
|
+
_create_batch(VPNPhase1PolicyFactory, 20)
|
|
342
|
+
_create_batch(VPNPhase2PolicyFactory, 20)
|
|
343
|
+
_create_batch(VPNProfileFactory, 30)
|
|
344
344
|
_create_batch(VPNFactory, 10)
|
|
345
345
|
_create_batch(VPNTunnelEndpointFactory, 20)
|
|
346
346
|
_create_batch(VPNTunnelFactory, 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/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)
|