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/ui/package-lock.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"graphiql": "2.4.7",
|
|
18
18
|
"graphql": "16.10.0",
|
|
19
19
|
"graphql-ws": "5.13.1",
|
|
20
|
-
"highlight.js": "11.
|
|
20
|
+
"highlight.js": "^11.11.1",
|
|
21
21
|
"htmx.org": "^2.0.6",
|
|
22
22
|
"jquery": "^3.7.1",
|
|
23
23
|
"jquery-ui": "^1.14.1",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@eslint/js": "^9.32.0",
|
|
34
34
|
"autoprefixer": "^10.4.20",
|
|
35
|
+
"copy-webpack-plugin": "^13.0.1",
|
|
35
36
|
"css-loader": "^7.1.2",
|
|
36
37
|
"eslint": "^9.32.0",
|
|
37
38
|
"eslint-plugin-import": "^2.32.0",
|
|
@@ -2055,6 +2056,30 @@
|
|
|
2055
2056
|
"toggle-selection": "^1.0.6"
|
|
2056
2057
|
}
|
|
2057
2058
|
},
|
|
2059
|
+
"node_modules/copy-webpack-plugin": {
|
|
2060
|
+
"version": "13.0.1",
|
|
2061
|
+
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz",
|
|
2062
|
+
"integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==",
|
|
2063
|
+
"dev": true,
|
|
2064
|
+
"license": "MIT",
|
|
2065
|
+
"dependencies": {
|
|
2066
|
+
"glob-parent": "^6.0.1",
|
|
2067
|
+
"normalize-path": "^3.0.0",
|
|
2068
|
+
"schema-utils": "^4.2.0",
|
|
2069
|
+
"serialize-javascript": "^6.0.2",
|
|
2070
|
+
"tinyglobby": "^0.2.12"
|
|
2071
|
+
},
|
|
2072
|
+
"engines": {
|
|
2073
|
+
"node": ">= 18.12.0"
|
|
2074
|
+
},
|
|
2075
|
+
"funding": {
|
|
2076
|
+
"type": "opencollective",
|
|
2077
|
+
"url": "https://opencollective.com/webpack"
|
|
2078
|
+
},
|
|
2079
|
+
"peerDependencies": {
|
|
2080
|
+
"webpack": "^5.1.0"
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2058
2083
|
"node_modules/cosmiconfig": {
|
|
2059
2084
|
"version": "9.0.0",
|
|
2060
2085
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
|
|
@@ -3507,9 +3532,9 @@
|
|
|
3507
3532
|
}
|
|
3508
3533
|
},
|
|
3509
3534
|
"node_modules/highlight.js": {
|
|
3510
|
-
"version": "11.
|
|
3511
|
-
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.
|
|
3512
|
-
"integrity": "sha512-
|
|
3535
|
+
"version": "11.11.1",
|
|
3536
|
+
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
|
3537
|
+
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
|
3513
3538
|
"license": "BSD-3-Clause",
|
|
3514
3539
|
"engines": {
|
|
3515
3540
|
"node": ">=12.0.0"
|
|
@@ -4467,6 +4492,16 @@
|
|
|
4467
4492
|
"dev": true,
|
|
4468
4493
|
"license": "MIT"
|
|
4469
4494
|
},
|
|
4495
|
+
"node_modules/normalize-path": {
|
|
4496
|
+
"version": "3.0.0",
|
|
4497
|
+
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
|
4498
|
+
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
|
4499
|
+
"dev": true,
|
|
4500
|
+
"license": "MIT",
|
|
4501
|
+
"engines": {
|
|
4502
|
+
"node": ">=0.10.0"
|
|
4503
|
+
}
|
|
4504
|
+
},
|
|
4470
4505
|
"node_modules/normalize-range": {
|
|
4471
4506
|
"version": "0.1.2",
|
|
4472
4507
|
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
|
@@ -6028,6 +6063,54 @@
|
|
|
6028
6063
|
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
|
|
6029
6064
|
"license": "MIT"
|
|
6030
6065
|
},
|
|
6066
|
+
"node_modules/tinyglobby": {
|
|
6067
|
+
"version": "0.2.15",
|
|
6068
|
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
|
6069
|
+
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
|
6070
|
+
"dev": true,
|
|
6071
|
+
"license": "MIT",
|
|
6072
|
+
"dependencies": {
|
|
6073
|
+
"fdir": "^6.5.0",
|
|
6074
|
+
"picomatch": "^4.0.3"
|
|
6075
|
+
},
|
|
6076
|
+
"engines": {
|
|
6077
|
+
"node": ">=12.0.0"
|
|
6078
|
+
},
|
|
6079
|
+
"funding": {
|
|
6080
|
+
"url": "https://github.com/sponsors/SuperchupuDev"
|
|
6081
|
+
}
|
|
6082
|
+
},
|
|
6083
|
+
"node_modules/tinyglobby/node_modules/fdir": {
|
|
6084
|
+
"version": "6.5.0",
|
|
6085
|
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
|
6086
|
+
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
|
6087
|
+
"dev": true,
|
|
6088
|
+
"license": "MIT",
|
|
6089
|
+
"engines": {
|
|
6090
|
+
"node": ">=12.0.0"
|
|
6091
|
+
},
|
|
6092
|
+
"peerDependencies": {
|
|
6093
|
+
"picomatch": "^3 || ^4"
|
|
6094
|
+
},
|
|
6095
|
+
"peerDependenciesMeta": {
|
|
6096
|
+
"picomatch": {
|
|
6097
|
+
"optional": true
|
|
6098
|
+
}
|
|
6099
|
+
}
|
|
6100
|
+
},
|
|
6101
|
+
"node_modules/tinyglobby/node_modules/picomatch": {
|
|
6102
|
+
"version": "4.0.3",
|
|
6103
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
|
6104
|
+
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
|
6105
|
+
"dev": true,
|
|
6106
|
+
"license": "MIT",
|
|
6107
|
+
"engines": {
|
|
6108
|
+
"node": ">=12"
|
|
6109
|
+
},
|
|
6110
|
+
"funding": {
|
|
6111
|
+
"url": "https://github.com/sponsors/jonschlinkert"
|
|
6112
|
+
}
|
|
6113
|
+
},
|
|
6031
6114
|
"node_modules/to-regex-range": {
|
|
6032
6115
|
"version": "5.0.1",
|
|
6033
6116
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
nautobot/ui/package.json
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"graphiql": "2.4.7",
|
|
24
24
|
"graphql": "16.10.0",
|
|
25
25
|
"graphql-ws": "5.13.1",
|
|
26
|
-
"highlight.js": "11.
|
|
26
|
+
"highlight.js": "^11.11.1",
|
|
27
27
|
"htmx.org": "^2.0.6",
|
|
28
28
|
"jquery": "^3.7.1",
|
|
29
29
|
"jquery-ui": "^1.14.1",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@eslint/js": "^9.32.0",
|
|
40
40
|
"autoprefixer": "^10.4.20",
|
|
41
|
+
"copy-webpack-plugin": "^13.0.1",
|
|
41
42
|
"css-loader": "^7.1.2",
|
|
42
43
|
"eslint": "^9.32.0",
|
|
43
44
|
"eslint-plugin-import": "^2.32.0",
|
nautobot/ui/src/js/nautobot.js
CHANGED
|
@@ -15,7 +15,6 @@ import flatpickr from 'flatpickr';
|
|
|
15
15
|
window.flatpickr = flatpickr;
|
|
16
16
|
|
|
17
17
|
import hljs from 'highlight.js/lib/core';
|
|
18
|
-
import 'highlight.js/styles/github.css';
|
|
19
18
|
import graphql from 'highlight.js/lib/languages/graphql';
|
|
20
19
|
import json from 'highlight.js/lib/languages/json';
|
|
21
20
|
import xml from 'highlight.js/lib/languages/xml';
|
nautobot/ui/src/js/select2.js
CHANGED
|
@@ -81,7 +81,7 @@ const initializeSelect2 = (context, selector, options) =>
|
|
|
81
81
|
[...getElement(context).querySelectorAll(selector)].forEach((element) =>
|
|
82
82
|
$(element).select2({
|
|
83
83
|
allowClear: true,
|
|
84
|
-
placeholder:
|
|
84
|
+
placeholder: '---------',
|
|
85
85
|
selectionCssClass: 'select2--small',
|
|
86
86
|
theme: 'bootstrap-5',
|
|
87
87
|
width: 'off',
|
|
@@ -292,14 +292,65 @@ const initializeDynamicChoiceSelection = (context, dropdownParent = null) => {
|
|
|
292
292
|
});
|
|
293
293
|
};
|
|
294
294
|
|
|
295
|
-
const initializeMultiValueChar = (context, dropdownParent = null) =>
|
|
295
|
+
const initializeMultiValueChar = (context, dropdownParent = null) => {
|
|
296
296
|
initializeSelect2(context, '.nautobot-select2-multi-value-char', {
|
|
297
297
|
dropdownParent,
|
|
298
298
|
language: { noResults: () => 'Type something to add it as an option' },
|
|
299
299
|
multiple: true,
|
|
300
300
|
tags: true,
|
|
301
|
+
tokenSeparators: [','],
|
|
301
302
|
});
|
|
302
303
|
|
|
304
|
+
// Ensure pressing Enter in the Select2 search adds the current token instead of submitting the form
|
|
305
|
+
[...getElement(context).querySelectorAll('.nautobot-select2-multi-value-char')].forEach((element) => {
|
|
306
|
+
$(element).on('select2:open', () => {
|
|
307
|
+
const container = document.querySelector('.select2-container--open');
|
|
308
|
+
if (!container) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const search = container.querySelector('input.select2-search__field');
|
|
312
|
+
if (!search) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Avoid stacking multiple handlers
|
|
317
|
+
if (search.getAttribute('data-enter-binds')) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
search.setAttribute('data-enter-binds', '1');
|
|
321
|
+
|
|
322
|
+
search.addEventListener('keydown', (ev) => {
|
|
323
|
+
if (ev.key === 'Enter') {
|
|
324
|
+
ev.preventDefault();
|
|
325
|
+
ev.stopPropagation();
|
|
326
|
+
const val = search.value.trim();
|
|
327
|
+
if (!val) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const sel = $(element).get(0);
|
|
331
|
+
// If option doesn't exist, create it; otherwise select it
|
|
332
|
+
const found = Array.prototype.find.call(sel.options, (opt) => String(opt.value) === String(val));
|
|
333
|
+
if (found) {
|
|
334
|
+
found.selected = true;
|
|
335
|
+
} else {
|
|
336
|
+
sel.add(new Option(val, val, true, true));
|
|
337
|
+
}
|
|
338
|
+
// Clear the search box and notify Select2
|
|
339
|
+
search.value = '';
|
|
340
|
+
$(element).trigger('change');
|
|
341
|
+
// Close the dropdown so it doesn't linger after add
|
|
342
|
+
try {
|
|
343
|
+
$(element).select2('close');
|
|
344
|
+
// eslint-disable-next-line no-unused-vars
|
|
345
|
+
} catch (exception) {
|
|
346
|
+
// Intentional no-op
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
};
|
|
353
|
+
|
|
303
354
|
const initializeStaticChoiceSelection = (context, dropdownParent = null) =>
|
|
304
355
|
initializeSelect2(context, '.nautobot-select2-static', { dropdownParent });
|
|
305
356
|
|
|
@@ -907,6 +907,12 @@ $table-variants: (
|
|
|
907
907
|
|
|
908
908
|
color: var(--#{$prefix}table-color);
|
|
909
909
|
border-color: var(--#{$prefix}table-border-color);
|
|
910
|
+
|
|
911
|
+
@if $color == "primary" or $color == "info" {
|
|
912
|
+
a {
|
|
913
|
+
text-decoration: underline;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
910
916
|
}
|
|
911
917
|
}
|
|
912
918
|
|
|
@@ -915,6 +921,10 @@ $table-variants: (
|
|
|
915
921
|
white-space: nowrap;
|
|
916
922
|
}
|
|
917
923
|
|
|
924
|
+
table.attr-table td:nth-child(1) {
|
|
925
|
+
width: 25%;
|
|
926
|
+
}
|
|
927
|
+
|
|
918
928
|
/* Buttons */
|
|
919
929
|
.btn {
|
|
920
930
|
--#{$prefix}btn-box-shadow: #{$btn-box-shadow};
|
|
@@ -1262,6 +1272,18 @@ textarea {
|
|
|
1262
1272
|
}
|
|
1263
1273
|
}
|
|
1264
1274
|
|
|
1275
|
+
.card-body {
|
|
1276
|
+
.nav-tabs {
|
|
1277
|
+
margin-inline: $card-spacer-x * -1;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
.tab-content {
|
|
1281
|
+
> .tab-pane {
|
|
1282
|
+
padding-block-start: $card-spacer-y;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1265
1287
|
.card-header {
|
|
1266
1288
|
position: relative;
|
|
1267
1289
|
text-transform: uppercase;
|
|
@@ -1397,6 +1419,11 @@ textarea {
|
|
|
1397
1419
|
}
|
|
1398
1420
|
}
|
|
1399
1421
|
|
|
1422
|
+
/* Alerts */
|
|
1423
|
+
.alert:is(.alert-primary, .alert-info) a {
|
|
1424
|
+
text-decoration: underline;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1400
1427
|
/* Progress bars */
|
|
1401
1428
|
.progress {
|
|
1402
1429
|
td & {
|
|
@@ -1656,7 +1683,7 @@ body {
|
|
|
1656
1683
|
}
|
|
1657
1684
|
|
|
1658
1685
|
img {
|
|
1659
|
-
height:
|
|
1686
|
+
height: 2rem; /* 32px */
|
|
1660
1687
|
}
|
|
1661
1688
|
}
|
|
1662
1689
|
|
|
@@ -1876,7 +1903,7 @@ body {
|
|
|
1876
1903
|
@include fade-in;
|
|
1877
1904
|
@include transition($transition-base);
|
|
1878
1905
|
right: -0.8125rem; /* -13px */
|
|
1879
|
-
top: 1.
|
|
1906
|
+
top: 1.4375rem; /* 23px */
|
|
1880
1907
|
|
|
1881
1908
|
&::before { /* Chevron icon */
|
|
1882
1909
|
@include chevron(1rem, $navy-0); /* 16px */
|
|
@@ -2354,6 +2381,11 @@ pre code.hljs {
|
|
|
2354
2381
|
border-radius: $border-radius;
|
|
2355
2382
|
}
|
|
2356
2383
|
|
|
2384
|
+
/* Swagger docs */
|
|
2385
|
+
pre.version, pre.version-stamp {
|
|
2386
|
+
background-color: inherit !important;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2357
2389
|
/* Rendered Markdown */
|
|
2358
2390
|
.nb-rendered-markdown {
|
|
2359
2391
|
table {
|
|
@@ -2469,3 +2501,20 @@ pre code.hljs {
|
|
|
2469
2501
|
.d-none {
|
|
2470
2502
|
display: none !important;
|
|
2471
2503
|
}
|
|
2504
|
+
|
|
2505
|
+
/* Dark mode workaround */
|
|
2506
|
+
@include color-mode(dark, true) {
|
|
2507
|
+
.invert-in-dark-mode {
|
|
2508
|
+
filter: invert(1) hue-rotate(180deg);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
/*
|
|
2513
|
+
* FIXME(norbert-mieczkowski-codilime): bootstrap-filestyle library, which is used by `ClearableFileInput` widget,
|
|
2514
|
+
* currently in version 1.2.3, still uses Bootstrap 3. This `.hidden` class "polyfill" is required until the library
|
|
2515
|
+
* is updated or removed/replaced completely. Bootstrap 5 supports file inputs out of the box, so it may be a good
|
|
2516
|
+
* time to get rid of bootstrap-filestyle.
|
|
2517
|
+
*/
|
|
2518
|
+
.hidden {
|
|
2519
|
+
@extend .d-none;
|
|
2520
|
+
}
|
nautobot/ui/webpack.config.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import autoprefixer from 'autoprefixer';
|
|
5
|
+
import CopyPlugin from 'copy-webpack-plugin';
|
|
5
6
|
import miniCssExtractPlugin from 'mini-css-extract-plugin'
|
|
6
7
|
|
|
7
8
|
const __dirname = import.meta.dirname;
|
|
@@ -24,6 +25,18 @@ export default [
|
|
|
24
25
|
}
|
|
25
26
|
},
|
|
26
27
|
plugins: [
|
|
28
|
+
new CopyPlugin({
|
|
29
|
+
patterns: [
|
|
30
|
+
{
|
|
31
|
+
from: path.resolve(__dirname, 'node_modules', 'highlight.js', 'styles', 'github.min.css'),
|
|
32
|
+
to: path.resolve(__dirname, '..', 'project-static', 'dist', 'css'),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
from: path.resolve(__dirname, 'node_modules', 'highlight.js', 'styles', 'github-dark.min.css'),
|
|
36
|
+
to: path.resolve(__dirname, '..', 'project-static', 'dist', 'css'),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
}),
|
|
27
40
|
new miniCssExtractPlugin(
|
|
28
41
|
{
|
|
29
42
|
filename: 'css/[name].css'
|
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
<table class="table table-striped">
|
|
14
14
|
<thead>
|
|
15
15
|
<tr>
|
|
16
|
-
<th
|
|
16
|
+
<th>
|
|
17
|
+
<input class="form-check-input nb-form-check-input-sm mt-2 toggle"
|
|
18
|
+
title="Toggle all"
|
|
19
|
+
type="checkbox">
|
|
20
|
+
</th>
|
|
17
21
|
<th>Preference</th>
|
|
18
22
|
<th>Value</th>
|
|
19
23
|
</tr>
|
|
@@ -21,7 +25,12 @@
|
|
|
21
25
|
<tbody>
|
|
22
26
|
{% for key, value in preferences.items %}
|
|
23
27
|
<tr>
|
|
24
|
-
<td class="nb-w-0"
|
|
28
|
+
<td class="nb-w-0">
|
|
29
|
+
<input class="form-check-input nb-form-check-input-sm mt-2"
|
|
30
|
+
name="pk"
|
|
31
|
+
type="checkbox"
|
|
32
|
+
value="{{ key }}">
|
|
33
|
+
</td>
|
|
25
34
|
<td><samp>{{ key }}</samp></td>
|
|
26
35
|
<td><samp>{{ value }}</samp></td>
|
|
27
36
|
</tr>
|
|
@@ -22,7 +22,7 @@ from nautobot.extras.filters import (
|
|
|
22
22
|
RoleModelFilterSetMixin,
|
|
23
23
|
StatusModelFilterSetMixin,
|
|
24
24
|
)
|
|
25
|
-
from nautobot.ipam.models import IPAddress, Service, VLAN
|
|
25
|
+
from nautobot.ipam.models import IPAddress, Service, VLAN, VRF
|
|
26
26
|
from nautobot.tenancy.filters import TenancyModelFilterSetMixin
|
|
27
27
|
|
|
28
28
|
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
|
@@ -224,6 +224,11 @@ class VirtualMachineFilterSet(
|
|
|
224
224
|
distinct=True,
|
|
225
225
|
)
|
|
226
226
|
has_ip_addresses = RelatedMembershipBooleanFilter(field_name="interfaces__ip_addresses", label="Has IP addresses")
|
|
227
|
+
vrfs = NaturalKeyOrPKMultipleChoiceFilter(
|
|
228
|
+
queryset=VRF.objects.all(),
|
|
229
|
+
to_field_name="rd",
|
|
230
|
+
label="VRFs (ID or RD)",
|
|
231
|
+
)
|
|
227
232
|
|
|
228
233
|
def filter_ip_addresses(self, queryset, name, value):
|
|
229
234
|
pk_values = set(item for item in value if is_uuid(item))
|
|
@@ -3,7 +3,7 @@ from nautobot.dcim.choices import InterfaceModeChoices
|
|
|
3
3
|
from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer, Platform, SoftwareVersion
|
|
4
4
|
from nautobot.extras.models import Role, Status, Tag
|
|
5
5
|
from nautobot.ipam.choices import ServiceProtocolChoices
|
|
6
|
-
from nautobot.ipam.models import IPAddress, Namespace, Prefix, Service, VLAN
|
|
6
|
+
from nautobot.ipam.models import IPAddress, Namespace, Prefix, Service, VLAN, VRF, VRFDeviceAssignment
|
|
7
7
|
from nautobot.tenancy.models import Tenant
|
|
8
8
|
from nautobot.virtualization.filters import (
|
|
9
9
|
ClusterFilterSet,
|
|
@@ -238,6 +238,8 @@ class VirtualMachineTestCase(FilterTestCases.FilterTestCase, FilterTestCases.Ten
|
|
|
238
238
|
("status", "status__id"),
|
|
239
239
|
("status", "status__name"),
|
|
240
240
|
("vcpus",),
|
|
241
|
+
("vrfs", "vrfs__id"),
|
|
242
|
+
("vrfs", "vrfs__rd"),
|
|
241
243
|
)
|
|
242
244
|
|
|
243
245
|
@classmethod
|
|
@@ -446,6 +448,13 @@ class VirtualMachineTestCase(FilterTestCases.FilterTestCase, FilterTestCases.Ten
|
|
|
446
448
|
vms[0].tags.set(Tag.objects.get_for_model(VirtualMachine))
|
|
447
449
|
vms[1].tags.set(Tag.objects.get_for_model(VirtualMachine)[:3])
|
|
448
450
|
|
|
451
|
+
vrfs = (
|
|
452
|
+
VRF.objects.create(name="VRF 1", rd="1:1"),
|
|
453
|
+
VRF.objects.create(name="VRF 2", rd="1:2"),
|
|
454
|
+
)
|
|
455
|
+
VRFDeviceAssignment.objects.create(virtual_machine=vms[0], vrf=vrfs[0])
|
|
456
|
+
VRFDeviceAssignment.objects.create(virtual_machine=vms[1], vrf=vrfs[1])
|
|
457
|
+
|
|
449
458
|
def test_filters_generic(self):
|
|
450
459
|
# Assign more than 2 different software versions to VirtualMachine before we test generic filters
|
|
451
460
|
software_versions = list(SoftwareVersion.objects.all())
|
|
@@ -145,6 +145,7 @@ class VMInterfaceTestCase(TestCase): # TODO: change to BaseModelTestCase
|
|
|
145
145
|
name="Int1", virtual_machine=self.virtualmachine, status=self.int_status
|
|
146
146
|
)
|
|
147
147
|
ips = list(IPAddress.objects.all()[:10])
|
|
148
|
+
self.assertEqual(len(ips), 10)
|
|
148
149
|
|
|
149
150
|
# baseline (no vm_interface to ip address relationships exists)
|
|
150
151
|
self.assertFalse(IPAddressToInterface.objects.filter(vm_interface=vm_interface).exists())
|
nautobot/virtualization/views.py
CHANGED
|
@@ -191,6 +191,8 @@ class VirtualMachineUIViewSet(NautobotUIViewSet):
|
|
|
191
191
|
table_class=VRFDeviceAssignmentTable,
|
|
192
192
|
table_filter="virtual_machine",
|
|
193
193
|
exclude_columns=["related_object_type", "related_object_name"],
|
|
194
|
+
related_list_url_name="ipam:vrf_list",
|
|
195
|
+
related_field_name="virtual_machines",
|
|
194
196
|
),
|
|
195
197
|
object_detail.ObjectFieldsPanel(
|
|
196
198
|
weight=200,
|
|
@@ -262,6 +264,7 @@ class VirtualMachineUIViewSet(NautobotUIViewSet):
|
|
|
262
264
|
detail=True,
|
|
263
265
|
url_path="config-context",
|
|
264
266
|
url_name="configcontext",
|
|
267
|
+
custom_view_base_action="view",
|
|
265
268
|
custom_view_additional_permissions=["extras.view_configcontext"],
|
|
266
269
|
)
|
|
267
270
|
def config_context(self, request, pk):
|
|
@@ -288,7 +291,7 @@ class VirtualMachineUIViewSet(NautobotUIViewSet):
|
|
|
288
291
|
"source_contexts": ConfigContext.objects.restrict(request.user, "view").get_for_object(instance),
|
|
289
292
|
"format": data_format,
|
|
290
293
|
"template": "extras/object_configcontext.html",
|
|
291
|
-
"base_template": "
|
|
294
|
+
"base_template": "generic/object_retrieve.html",
|
|
292
295
|
}
|
|
293
296
|
|
|
294
297
|
return Response(context)
|
nautobot/vpn/factory.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
1
2
|
import factory
|
|
2
3
|
import faker
|
|
3
4
|
|
|
@@ -8,9 +9,9 @@ from nautobot.core.factory import (
|
|
|
8
9
|
random_instance,
|
|
9
10
|
UniqueFaker,
|
|
10
11
|
)
|
|
11
|
-
from nautobot.dcim.models import
|
|
12
|
+
from nautobot.dcim.models import Interface
|
|
12
13
|
from nautobot.extras.models import DynamicGroup, Role, SecretsGroup, Status
|
|
13
|
-
from nautobot.ipam.models import
|
|
14
|
+
from nautobot.ipam.models import Prefix
|
|
14
15
|
from nautobot.tenancy.models import Tenant
|
|
15
16
|
from nautobot.vpn import choices, models
|
|
16
17
|
|
|
@@ -163,24 +164,26 @@ class VPNTunnelFactory(PrimaryModelFactory):
|
|
|
163
164
|
class VPNTunnelEndpointFactory(PrimaryModelFactory):
|
|
164
165
|
class Meta:
|
|
165
166
|
model = models.VPNTunnelEndpoint
|
|
166
|
-
exclude = ("
|
|
167
|
+
exclude = ("has_source_interface", "has_profile", "has_role", "has_tenant")
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
device = factory.Maybe("has_device", random_instance(Device), None)
|
|
169
|
+
has_source_interface = NautobotBoolIterator()
|
|
170
170
|
source_interface = factory.Maybe(
|
|
171
|
-
"
|
|
171
|
+
"has_source_interface",
|
|
172
172
|
random_instance(
|
|
173
173
|
lambda: Interface.objects.filter(vpn_tunnel_endpoints_src_int__isnull=True, device__isnull=False)
|
|
174
174
|
),
|
|
175
175
|
None,
|
|
176
176
|
)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
177
|
+
source_fqdn = factory.Maybe("has_source_interface", "", factory.Faker("hostname"))
|
|
178
|
+
|
|
179
|
+
@factory.lazy_attribute
|
|
180
|
+
def tunnel_interface(self):
|
|
181
|
+
"""Filter tunnel interfaces on the same device as source_interface."""
|
|
182
|
+
if self.has_source_interface:
|
|
183
|
+
qs = Interface.objects.filter(type="tunnel", device=self.source_interface.device)
|
|
184
|
+
return factory.random.randgen.choice(qs) if qs.exists() else None
|
|
185
|
+
return None
|
|
186
|
+
|
|
184
187
|
has_profile = NautobotBoolIterator()
|
|
185
188
|
vpn_profile = factory.Maybe("has_profile", random_instance(models.VPNProfile), None)
|
|
186
189
|
has_role = NautobotBoolIterator()
|
|
@@ -198,7 +201,10 @@ class VPNTunnelEndpointFactory(PrimaryModelFactory):
|
|
|
198
201
|
if extracted:
|
|
199
202
|
self.protected_prefixes.set(extracted)
|
|
200
203
|
else:
|
|
201
|
-
|
|
204
|
+
# TODO Investigate https://github.com/nautobot/nautobot/actions/runs/11019738391/job/30603271529
|
|
205
|
+
# to uncomment the line below.
|
|
206
|
+
# self.protected_prefixes.set(get_random_instances(Prefix))
|
|
207
|
+
self.protected_prefixes.set(get_random_instances(model_or_queryset_or_lambda=Prefix, maximum=1))
|
|
202
208
|
|
|
203
209
|
@factory.post_generation
|
|
204
210
|
def protected_prefixes_dg(self, create, extracted, **kwargs):
|
|
@@ -206,4 +212,8 @@ class VPNTunnelEndpointFactory(PrimaryModelFactory):
|
|
|
206
212
|
if extracted:
|
|
207
213
|
self.protected_prefixes_dg.set(extracted)
|
|
208
214
|
else:
|
|
209
|
-
self.protected_prefixes_dg.set(
|
|
215
|
+
self.protected_prefixes_dg.set(
|
|
216
|
+
get_random_instances(
|
|
217
|
+
DynamicGroup.objects.filter(content_type=ContentType.objects.get_for_model(Prefix)), minimum=0
|
|
218
|
+
)
|
|
219
|
+
)
|
nautobot/vpn/filters.py
CHANGED
nautobot/vpn/forms.py
CHANGED
|
@@ -446,6 +446,7 @@ class VPNTunnelEndpointForm(NautobotModelForm, TenancyForm): # pylint: disable=
|
|
|
446
446
|
required=False,
|
|
447
447
|
label="Protected Prefixes Dynamic Group",
|
|
448
448
|
to_field_name="name",
|
|
449
|
+
query_params={"content_type": "ipam.prefix"},
|
|
449
450
|
help_text="Protected Prefixes behind the tunnel endpoint.",
|
|
450
451
|
)
|
|
451
452
|
|
|
@@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
|
|
19
19
|
("tenancy", "0009_update_all_charfields_max_length_to_255"),
|
|
20
20
|
("extras", "0130_jobresult_generate_log_entry_counts"),
|
|
21
21
|
("ipam", "0053_alter_vrfdeviceassignment_options_and_more"),
|
|
22
|
-
("dcim", "
|
|
22
|
+
("dcim", "0080_device_name_data_migration"),
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
operations = [
|
nautobot/vpn/models.py
CHANGED
|
@@ -497,11 +497,7 @@ class VPNTunnelEndpoint(PrimaryModel): # pylint: disable=too-many-ancestors
|
|
|
497
497
|
def _name(self):
|
|
498
498
|
"""Dynamic name field."""
|
|
499
499
|
if self.source_interface:
|
|
500
|
-
|
|
501
|
-
parent_intf = f"{self.source_interface.parent.name} {self.source_interface.name}"
|
|
502
|
-
else:
|
|
503
|
-
# Interface on a module that isn't installed in a device at present
|
|
504
|
-
parent_intf = f"{self.source_interface.module} {self.source_interface.name}"
|
|
500
|
+
parent_intf = f"{self.source_interface.parent.name} {self.source_interface.name}"
|
|
505
501
|
if self.source_ipaddress:
|
|
506
502
|
return f"{parent_intf} ({self.source_ipaddress.address})"
|
|
507
503
|
return parent_intf
|
|
@@ -516,12 +512,24 @@ class VPNTunnelEndpoint(PrimaryModel): # pylint: disable=too-many-ancestors
|
|
|
516
512
|
raise ValidationError("Source IP Address and Source FQDN are mutually exclusive fields. Select only one.")
|
|
517
513
|
if not any([self.source_interface, self.source_ipaddress, self.source_fqdn]):
|
|
518
514
|
raise ValidationError("Source Interface or Source IP Address or Source FQDN Is required.")
|
|
519
|
-
if self.source_interface and not self.source_interface.
|
|
515
|
+
if self.source_interface and not self.source_interface.parent:
|
|
520
516
|
raise ValidationError("Source Interface must belong to a device.")
|
|
517
|
+
if (
|
|
518
|
+
self.source_ipaddress
|
|
519
|
+
and self.source_interface
|
|
520
|
+
and (self.source_ipaddress not in self.source_interface.ip_addresses.all())
|
|
521
|
+
):
|
|
522
|
+
raise ValidationError("Source IP address must be assigned to Source Interface.")
|
|
523
|
+
if (
|
|
524
|
+
self.tunnel_interface
|
|
525
|
+
and self.source_interface
|
|
526
|
+
and (self.tunnel_interface not in self.source_interface.parent.all_interfaces)
|
|
527
|
+
):
|
|
528
|
+
raise ValidationError("Tunnel Interface and Source Interface must be on the same device")
|
|
521
529
|
return super().clean()
|
|
522
530
|
|
|
523
531
|
def save(self, *args, **kwargs):
|
|
524
|
-
if self.source_interface
|
|
525
|
-
self.device = self.source_interface.
|
|
532
|
+
if self.source_interface:
|
|
533
|
+
self.device = self.source_interface.parent
|
|
526
534
|
self.name = self._name()
|
|
527
535
|
super().save(*args, **kwargs)
|
nautobot/vpn/tables.py
CHANGED
|
@@ -5,6 +5,7 @@ import django_tables2 as tables
|
|
|
5
5
|
|
|
6
6
|
from nautobot.apps.tables import (
|
|
7
7
|
BaseTable,
|
|
8
|
+
BooleanColumn,
|
|
8
9
|
ButtonsColumn,
|
|
9
10
|
LinkedCountColumn,
|
|
10
11
|
RoleTableMixin,
|
|
@@ -25,17 +26,19 @@ class VPNProfileTable(RoleTableMixin, BaseTable):
|
|
|
25
26
|
name = tables.Column(linkify=True)
|
|
26
27
|
vpn_phase1_policy_count = LinkedCountColumn(
|
|
27
28
|
viewname="vpn:vpnphase1policy_list",
|
|
28
|
-
verbose_name="
|
|
29
|
+
verbose_name="Phase 1 Policies",
|
|
29
30
|
url_params={"vpn_profiles": "pk"},
|
|
30
31
|
)
|
|
31
32
|
vpn_phase2_policy_count = LinkedCountColumn(
|
|
32
33
|
viewname="vpn:vpnphase2policy_list",
|
|
33
|
-
verbose_name="
|
|
34
|
+
verbose_name="Phase 2 Policies",
|
|
34
35
|
url_params={"vpn_profiles": "pk"},
|
|
35
36
|
)
|
|
36
37
|
tenant = TenantColumn()
|
|
37
38
|
actions = ButtonsColumn(models.VPNProfile)
|
|
38
39
|
tags = TagColumn(url_name="vpn:vpnprofile_list")
|
|
40
|
+
keepalive_enabled = BooleanColumn()
|
|
41
|
+
nat_traversal = BooleanColumn()
|
|
39
42
|
|
|
40
43
|
class Meta(BaseTable.Meta):
|
|
41
44
|
"""Meta attributes."""
|