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
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
{% render_field form.locations %}
|
|
19
19
|
{% render_field form.roles %}
|
|
20
20
|
{% render_field form.device_types %}
|
|
21
|
+
{% render_field form.device_families %}
|
|
21
22
|
{% render_field form.platforms %}
|
|
22
23
|
{% render_field form.cluster_groups %}
|
|
23
24
|
{% render_field form.clusters %}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
<div class="tab-content">
|
|
44
44
|
<div class="tab-pane active" id="filter-form">
|
|
45
45
|
{% if filter_form %}
|
|
46
|
-
<span class="form-text
|
|
46
|
+
<span class="form-text mb-12">
|
|
47
47
|
Select the filtering criteria to determine membership of objects matching
|
|
48
48
|
the Content Type for this Dynamic Group. Fields that are not a dropdown are
|
|
49
49
|
expected to have string inputs and do not support multiple values.
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
{% if app_data.availability %}
|
|
71
71
|
<tr>
|
|
72
72
|
<td>License</td>
|
|
73
|
-
<td><label class="badge
|
|
73
|
+
<td><label class="badge border border-primary text-primary">{{ app_data.availability }}</label></td>
|
|
74
74
|
</tr>
|
|
75
75
|
{% endif %}
|
|
76
76
|
</table>
|
|
@@ -82,11 +82,11 @@
|
|
|
82
82
|
<table class="table table-hover card-body attr-table">
|
|
83
83
|
<tr>
|
|
84
84
|
<td>Min Nautobot Version</td>
|
|
85
|
-
<td>v{{ object.min_version |
|
|
85
|
+
<td>{% if object.min_version %}v{{ object.min_version }}{% else %}{{ None|placeholder }}{% endif %}</td>
|
|
86
86
|
</tr>
|
|
87
87
|
<tr>
|
|
88
88
|
<td>Max Nautobot Version</td>
|
|
89
|
-
<td>v{{ object.max_version |
|
|
89
|
+
<td>{% if object.max_version %}v{{ object.max_version }}{% else %}{{ None|placeholder }}{% endif %}</td>
|
|
90
90
|
</tr>
|
|
91
91
|
</table>
|
|
92
92
|
</div>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<div class="card">
|
|
14
14
|
<div class="card-header"><strong>Parameters</strong></div>
|
|
15
15
|
<div class="card-body">
|
|
16
|
-
<span class="form-text">
|
|
16
|
+
<span class="form-text mb-12">
|
|
17
17
|
All parameter values can be entered as Jinja2 templates if desired.
|
|
18
18
|
The requesting object (device, Git repository, etc.), if any, will be provided
|
|
19
19
|
as context variable <code>obj</code>.
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
from django.contrib.contenttypes.models import ContentType
|
|
2
|
-
from django.test import tag
|
|
3
2
|
from django.urls import reverse
|
|
4
3
|
|
|
5
|
-
from nautobot.core.testing.integration import SeleniumTestCase
|
|
4
|
+
from nautobot.core.testing.integration import ObjectDetailsMixin, ObjectsListMixin, SeleniumTestCase
|
|
6
5
|
from nautobot.dcim.models import Device
|
|
7
6
|
from nautobot.extras.models import ComputedField
|
|
8
7
|
|
|
9
8
|
from . import create_test_device
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
class ComputedFieldsTestCase(SeleniumTestCase):
|
|
11
|
+
class ComputedFieldsTestCase(SeleniumTestCase, ObjectDetailsMixin, ObjectsListMixin):
|
|
13
12
|
"""
|
|
14
13
|
Integration test to check nautobot.extras.models.ComputedField.advanced_ui functionality
|
|
15
14
|
and computed fields appearing in an object list
|
|
@@ -26,7 +25,6 @@ class ComputedFieldsTestCase(SeleniumTestCase):
|
|
|
26
25
|
template="{{ obj.name }} is awesome!",
|
|
27
26
|
)
|
|
28
27
|
|
|
29
|
-
@tag("fix_in_v3")
|
|
30
28
|
def test_computed_field_advanced_ui(self):
|
|
31
29
|
"""
|
|
32
30
|
This test creates a device and a computed field for that device.
|
|
@@ -41,7 +39,7 @@ class ComputedFieldsTestCase(SeleniumTestCase):
|
|
|
41
39
|
self.assertTrue(self.browser.is_text_present("Device Computed Field"))
|
|
42
40
|
self.assertTrue(self.browser.is_text_present(f"{self.device.name} is awesome!"))
|
|
43
41
|
# # Check the computed field does NOT appear in the advanced tab
|
|
44
|
-
self.
|
|
42
|
+
self.switch_tab("Advanced")
|
|
45
43
|
self.assertFalse(self.browser.is_text_present("Device Computed Field"))
|
|
46
44
|
self.assertFalse(self.browser.is_text_present(f"{self.device.name} is awesome!"))
|
|
47
45
|
# Set the custom_field to only show in the advanced tab
|
|
@@ -53,18 +51,20 @@ class ComputedFieldsTestCase(SeleniumTestCase):
|
|
|
53
51
|
self.assertFalse(self.browser.is_text_present("Device Computed Field"))
|
|
54
52
|
self.assertFalse(self.browser.is_text_present(f"{self.device.name} is awesome!"))
|
|
55
53
|
# Check the computed field appears in the advanced tab
|
|
56
|
-
self.
|
|
54
|
+
self.switch_tab("Advanced")
|
|
57
55
|
self.assertTrue(self.browser.is_text_present("Device Computed Field"))
|
|
58
56
|
self.assertTrue(self.browser.is_text_present(f"{self.device.name} is awesome!"))
|
|
59
57
|
|
|
60
|
-
@tag("fix_in_v3")
|
|
61
58
|
def test_computed_field_appears_in_object_list(self):
|
|
62
59
|
"""
|
|
63
60
|
This test sets the computed field to be visible on the object list
|
|
64
61
|
and then checks to see if it actually appears there.
|
|
65
62
|
"""
|
|
66
63
|
self.browser.visit(f"{self.live_server_url}/dcim/devices/")
|
|
67
|
-
self.click_button("
|
|
64
|
+
self.click_button("""button[title="Configure table"]""")
|
|
65
|
+
nb_drawer_open_xpath = "//section[contains(@class, 'nb-drawer') and contains(@class, 'nb-drawer-open')]"
|
|
66
|
+
self.assertTrue(self.browser.is_element_present_by_xpath(nb_drawer_open_xpath, wait_time=5))
|
|
67
|
+
self.assertTrue(self.browser.find_by_xpath(nb_drawer_open_xpath).is_visible(wait_time=5))
|
|
68
68
|
select_option = self.browser.find_by_xpath(
|
|
69
69
|
".//select[@id='id_columns']/option[contains(text(), 'Device Computed Field')]"
|
|
70
70
|
)
|
|
@@ -72,6 +72,5 @@ class ComputedFieldsTestCase(SeleniumTestCase):
|
|
|
72
72
|
select_option.click()
|
|
73
73
|
self.browser.find_by_xpath(".//input[@value='Save']").click()
|
|
74
74
|
self.assertTrue(self.browser.is_text_present(f"{self.device.name} is awesome!"))
|
|
75
|
-
self.browser.find_by_xpath(".//button[@data-original-title='Configure table']").click()
|
|
76
75
|
self.browser.find_by_xpath(".//input[@value='Reset']").click()
|
|
77
76
|
self.assertFalse(self.browser.is_text_present(f"{self.device.name} is awesome!"))
|
|
@@ -304,9 +304,7 @@ class CustomFieldTestCase(SeleniumTestCase, ObjectDetailsMixin):
|
|
|
304
304
|
# Visit the device edit page
|
|
305
305
|
self.browser.visit(f"{self.live_server_url}{reverse('dcim:device_edit', kwargs={'pk': device.pk})}")
|
|
306
306
|
# Get the first item selected on the custom field
|
|
307
|
-
self.
|
|
308
|
-
'document.querySelector(\'label:has(+ * [name="cf_test_selection_field"])\').scrollIntoView({ behavior: "instant" });'
|
|
309
|
-
)
|
|
307
|
+
self.scroll_element_into_view(css='label:has(+ * [name="cf_test_selection_field"])')
|
|
310
308
|
self.browser.find_by_xpath(".//label[contains(text(), 'Device Selection Field')]").click()
|
|
311
309
|
active_web_element = self.browser.driver.switch_to.active_element
|
|
312
310
|
active_web_element.send_keys(Keys.ENTER)
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
from django.contrib.contenttypes.models import ContentType
|
|
2
|
-
from django.test import tag
|
|
3
2
|
from django.urls import reverse
|
|
4
3
|
from selenium.webdriver.common.keys import Keys
|
|
5
4
|
|
|
6
|
-
from nautobot.core.testing.integration import SeleniumTestCase
|
|
5
|
+
from nautobot.core.testing.integration import ObjectsListMixin, SeleniumTestCase
|
|
7
6
|
from nautobot.dcim.models import Device
|
|
8
7
|
from nautobot.extras.models import DynamicGroup
|
|
9
8
|
|
|
10
9
|
from . import create_test_device
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
class DynamicGroupTestCase(SeleniumTestCase):
|
|
12
|
+
class DynamicGroupTestCase(SeleniumTestCase, ObjectsListMixin):
|
|
14
13
|
"""
|
|
15
14
|
Integration test to check nautobot.extras.models.DynamicGroup add/edit functionality.
|
|
16
15
|
"""
|
|
@@ -19,7 +18,6 @@ class DynamicGroupTestCase(SeleniumTestCase):
|
|
|
19
18
|
super().setUp()
|
|
20
19
|
self.login_as_superuser()
|
|
21
20
|
|
|
22
|
-
@tag("fix_in_v3")
|
|
23
21
|
def test_create_and_update(self):
|
|
24
22
|
"""
|
|
25
23
|
Test initial add and then update of a new DynamicGroup
|
|
@@ -32,7 +30,7 @@ class DynamicGroupTestCase(SeleniumTestCase):
|
|
|
32
30
|
self.click_navbar_entry("Organization", "Dynamic Groups")
|
|
33
31
|
|
|
34
32
|
# Click add button
|
|
35
|
-
self.
|
|
33
|
+
self.click_add_item()
|
|
36
34
|
|
|
37
35
|
# Fill out the form.
|
|
38
36
|
name = "devices-active"
|
|
@@ -40,19 +38,20 @@ class DynamicGroupTestCase(SeleniumTestCase):
|
|
|
40
38
|
self.browser.select("content_type", ct_label)
|
|
41
39
|
|
|
42
40
|
# Click that "Create" button
|
|
43
|
-
self.
|
|
41
|
+
self.click_edit_form_create_button()
|
|
44
42
|
|
|
45
43
|
# Verify form redirect and presence of content.
|
|
46
44
|
self.assertTrue(self.browser.is_text_present(f"Created dynamic group {name}"))
|
|
47
45
|
self.assertTrue(self.browser.is_text_present("Edit"))
|
|
48
46
|
|
|
49
47
|
# Edit the newly created DynamicGroup (Click that "Edit" button)
|
|
50
|
-
self.
|
|
48
|
+
self.click_button("#edit-button")
|
|
51
49
|
|
|
52
50
|
# Find the "Status" dynamic multi-select and type into it. Xpath is used
|
|
53
51
|
# to find the next "input" after the "status" select field.
|
|
54
52
|
status_field = self.browser.find_by_name("filter-status").first
|
|
55
53
|
status_input = status_field.find_by_xpath("./following::input[1]").first
|
|
54
|
+
self.scroll_element_into_view(element=status_input)
|
|
56
55
|
status_input.click() # Force focus on the input field to bring it on-screen
|
|
57
56
|
|
|
58
57
|
# Fill in "Status: Active".
|
|
@@ -61,7 +60,7 @@ class DynamicGroupTestCase(SeleniumTestCase):
|
|
|
61
60
|
status_input.type(Keys.ENTER)
|
|
62
61
|
|
|
63
62
|
# Click that "Update" button
|
|
64
|
-
self.browser.
|
|
63
|
+
self.browser.find_by_xpath("//button[normalize-space()='Update']").click()
|
|
65
64
|
|
|
66
65
|
# Verify form redirect and presence of content.
|
|
67
66
|
self.assertTrue(self.browser.is_text_present(f"Modified dynamic group {name}"))
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from django.contrib.contenttypes.models import ContentType
|
|
2
|
-
from django.test import tag
|
|
3
2
|
from django.urls import reverse
|
|
4
3
|
|
|
5
4
|
from nautobot.core.testing.integration import ObjectDetailsMixin, SeleniumTestCase
|
|
@@ -19,7 +18,6 @@ class RelationshipsTestCase(SeleniumTestCase, ObjectDetailsMixin):
|
|
|
19
18
|
super().setUp()
|
|
20
19
|
self.login_as_superuser()
|
|
21
20
|
|
|
22
|
-
@tag("fix_in_v3")
|
|
23
21
|
def test_relationship_advanced_ui(self):
|
|
24
22
|
"""
|
|
25
23
|
This test creates a device and a relationship for that device.
|
|
@@ -2252,6 +2252,69 @@ class JobTest(
|
|
|
2252
2252
|
self.assertTrue(schedule.approval_required)
|
|
2253
2253
|
self.assertEqual(schedule.kwargs["var4"], str(device_role.pk))
|
|
2254
2254
|
|
|
2255
|
+
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
2256
|
+
@mock.patch("nautobot.extras.api.views.get_worker_count")
|
|
2257
|
+
@mock.patch("nautobot.extras.models.jobs.JobResult.enqueue_job")
|
|
2258
|
+
def test_run_job_filtered_approval(self, mock_enqueue_job, mock_get_worker_count):
|
|
2259
|
+
"""
|
|
2260
|
+
Run a job with a defined approval workflow whose filter should or should not include it.
|
|
2261
|
+
"""
|
|
2262
|
+
workflow = ApprovalWorkflowDefinition(
|
|
2263
|
+
name="Test Approval Workflow Definition 1",
|
|
2264
|
+
model_content_type=ContentType.objects.get_for_model(ScheduledJob),
|
|
2265
|
+
weight=0,
|
|
2266
|
+
model_constraints={"job_model__job_class_name": "APITestJob"},
|
|
2267
|
+
)
|
|
2268
|
+
workflow.validated_save()
|
|
2269
|
+
|
|
2270
|
+
# Do the stuff.
|
|
2271
|
+
mock_get_worker_count.return_value = 1
|
|
2272
|
+
self.add_permissions("extras.run_job")
|
|
2273
|
+
device_role = Role.objects.get_for_model(Device).first()
|
|
2274
|
+
job_data = {
|
|
2275
|
+
"var1": "FooBar",
|
|
2276
|
+
"var2": 123,
|
|
2277
|
+
"var3": False,
|
|
2278
|
+
"var4": device_role.pk,
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
data = {
|
|
2282
|
+
"data": job_data,
|
|
2283
|
+
# schedule is omitted
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
url = self.get_run_url()
|
|
2287
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
2288
|
+
self.assertHttpStatus(response, self.run_success_response_status)
|
|
2289
|
+
|
|
2290
|
+
# Assert that a JobResult for this job was NOT created.
|
|
2291
|
+
self.assertFalse(JobResult.objects.filter(name=self.job_model.name).exists())
|
|
2292
|
+
|
|
2293
|
+
# Assert that we have an immediate ScheduledJob and that it matches the job_model.
|
|
2294
|
+
schedule = ScheduledJob.objects.last()
|
|
2295
|
+
self.assertIsNotNone(schedule)
|
|
2296
|
+
self.assertEqual(schedule.interval, JobExecutionType.TYPE_FUTURE)
|
|
2297
|
+
self.assertTrue(schedule.approval_required)
|
|
2298
|
+
self.assertEqual(schedule.kwargs["var4"], str(device_role.pk))
|
|
2299
|
+
mock_enqueue_job.assert_not_called()
|
|
2300
|
+
|
|
2301
|
+
# Change the workflow definition so that it no longer applies to this job model
|
|
2302
|
+
workflow.model_constraints = {"job_model__job_class_name__istartswith": "SomeOtherJob"}
|
|
2303
|
+
workflow.validated_save()
|
|
2304
|
+
|
|
2305
|
+
mock_enqueue_job.return_value = None
|
|
2306
|
+
deserialized_data = self.job_class.deserialize_data(job_data)
|
|
2307
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
2308
|
+
self.assertHttpStatus(response, self.run_success_response_status)
|
|
2309
|
+
expected_enqueue_job_args = (self.job_model, self.user)
|
|
2310
|
+
expected_enqueue_job_kwargs = {
|
|
2311
|
+
"job_queue": self.job_model.default_job_queue,
|
|
2312
|
+
**self.job_class.serialize_data(deserialized_data),
|
|
2313
|
+
}
|
|
2314
|
+
mock_enqueue_job.assert_called_with(*expected_enqueue_job_args, **expected_enqueue_job_kwargs)
|
|
2315
|
+
# No new scheduled job should be created
|
|
2316
|
+
self.assertEqual(schedule, ScheduledJob.objects.last())
|
|
2317
|
+
|
|
2255
2318
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
2256
2319
|
@mock.patch("nautobot.extras.api.views.get_worker_count")
|
|
2257
2320
|
@mock.patch("nautobot.extras.models.jobs.JobResult.enqueue_job")
|
|
@@ -542,13 +542,35 @@ class ObjectChangeModelTest(TestCase): # TODO: change to BaseModelTestCase once
|
|
|
542
542
|
def setUpTestData(cls):
|
|
543
543
|
cls.location_status = Status.objects.get_for_model(Location).first()
|
|
544
544
|
|
|
545
|
+
def test_m2m_fields_not_excluded(self):
|
|
546
|
+
"""Ensure that m2m fields are included in object changes, even if exclude_m2m is the default in the REST API."""
|
|
547
|
+
with context_managers.web_request_context(self.user):
|
|
548
|
+
location_type = LocationType.objects.create(name="Test m2m locationtype")
|
|
549
|
+
|
|
550
|
+
with context_managers.web_request_context(self.user):
|
|
551
|
+
location_type.content_types.set(ContentType.objects.filter(app_label="dcim"))
|
|
552
|
+
|
|
553
|
+
object_changes = get_changes_for_model(location_type)
|
|
554
|
+
self.assertEqual(object_changes.count(), 2)
|
|
555
|
+
|
|
556
|
+
snapshots = object_changes.first().get_snapshots()
|
|
557
|
+
self.assertIsNotNone(snapshots["differences"]["removed"])
|
|
558
|
+
self.assertIsNotNone(snapshots["differences"]["added"])
|
|
559
|
+
self.assertIn("content_types", snapshots["differences"]["removed"])
|
|
560
|
+
self.assertIn("content_types", snapshots["differences"]["added"])
|
|
561
|
+
self.assertEqual(
|
|
562
|
+
len(snapshots["differences"]["added"]["content_types"]),
|
|
563
|
+
ContentType.objects.filter(app_label="dcim").count(),
|
|
564
|
+
)
|
|
565
|
+
|
|
545
566
|
def test_opt_out(self):
|
|
546
567
|
"""Hidden static group associations can "opt out" of change logging."""
|
|
547
568
|
dg = DynamicGroup.objects.exclude(group_type=DynamicGroupTypeChoices.TYPE_STATIC).first()
|
|
548
569
|
# Force reassignment of all cached memberships:
|
|
549
570
|
members = list(dg.members)
|
|
550
|
-
|
|
551
|
-
|
|
571
|
+
with context_managers.web_request_context(self.user):
|
|
572
|
+
dg._set_members([])
|
|
573
|
+
dg._set_members(members)
|
|
552
574
|
|
|
553
575
|
for sga in dg.static_group_associations(manager="all_objects").all():
|
|
554
576
|
self.assertIsNone(get_changes_for_model(sga).first())
|
|
@@ -14,6 +14,7 @@ from nautobot.core.testing import FilterTestCases
|
|
|
14
14
|
from nautobot.dcim.filters import DeviceFilterSet
|
|
15
15
|
from nautobot.dcim.models import (
|
|
16
16
|
Device,
|
|
17
|
+
DeviceFamily,
|
|
17
18
|
DeviceType,
|
|
18
19
|
Interface,
|
|
19
20
|
Location,
|
|
@@ -605,6 +606,8 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
|
|
|
605
606
|
("cluster_group", "cluster_groups__id"),
|
|
606
607
|
("cluster_group", "cluster_groups__name"),
|
|
607
608
|
("cluster_group_id", "cluster_groups__id"),
|
|
609
|
+
("device_family", "device_families__id"),
|
|
610
|
+
("device_family", "device_families__name"),
|
|
608
611
|
("device_type", "device_types__id"),
|
|
609
612
|
("device_type", "device_types__model"),
|
|
610
613
|
("device_type_id", "device_types__id"),
|
|
@@ -631,10 +634,28 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
|
|
|
631
634
|
|
|
632
635
|
manufacturer = Manufacturer.objects.first()
|
|
633
636
|
|
|
637
|
+
cls.device_families = (
|
|
638
|
+
DeviceFamily.objects.create(name="Device Family A"),
|
|
639
|
+
DeviceFamily.objects.create(name="Device Family B"),
|
|
640
|
+
DeviceFamily.objects.create(name="Device Family C"),
|
|
641
|
+
)
|
|
642
|
+
|
|
634
643
|
device_types = (
|
|
635
|
-
DeviceType.objects.create(
|
|
636
|
-
|
|
637
|
-
|
|
644
|
+
DeviceType.objects.create(
|
|
645
|
+
model="Device Type 1",
|
|
646
|
+
manufacturer=manufacturer,
|
|
647
|
+
device_family=cls.device_families[0],
|
|
648
|
+
),
|
|
649
|
+
DeviceType.objects.create(
|
|
650
|
+
model="Device Type 2",
|
|
651
|
+
manufacturer=manufacturer,
|
|
652
|
+
device_family=cls.device_families[1],
|
|
653
|
+
),
|
|
654
|
+
DeviceType.objects.create(
|
|
655
|
+
model="Device Type 3",
|
|
656
|
+
manufacturer=manufacturer,
|
|
657
|
+
device_family=cls.device_families[2],
|
|
658
|
+
),
|
|
638
659
|
)
|
|
639
660
|
cls.device_types = device_types
|
|
640
661
|
|
|
@@ -669,6 +690,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
|
|
|
669
690
|
)
|
|
670
691
|
c.locations.set([cls.locations[i]])
|
|
671
692
|
c.roles.set([device_roles[i]])
|
|
693
|
+
c.device_families.set([cls.device_families[i]])
|
|
672
694
|
c.device_types.set([device_types[i]])
|
|
673
695
|
c.platforms.set([platforms[i]])
|
|
674
696
|
c.cluster_groups.set([cluster_groups[i]])
|
|
@@ -2142,6 +2164,8 @@ class SecretTestCase(FilterTestCases.FilterTestCase):
|
|
|
2142
2164
|
("created",),
|
|
2143
2165
|
("last_updated",),
|
|
2144
2166
|
("name",),
|
|
2167
|
+
("secrets_groups", "secrets_groups__id"),
|
|
2168
|
+
("secrets_groups", "secrets_groups__name"),
|
|
2145
2169
|
("tags", "tags__id"),
|
|
2146
2170
|
("tags", "tags__name"),
|
|
2147
2171
|
)
|
|
@@ -2170,6 +2194,15 @@ class SecretTestCase(FilterTestCases.FilterTestCase):
|
|
|
2170
2194
|
secrets[0].tags.set(Tag.objects.get_for_model(Secret))
|
|
2171
2195
|
secrets[1].tags.set(Tag.objects.get_for_model(Secret)[:3])
|
|
2172
2196
|
|
|
2197
|
+
secrets_groups = (
|
|
2198
|
+
SecretsGroup.objects.create(
|
|
2199
|
+
name="Secrets Group 1",
|
|
2200
|
+
),
|
|
2201
|
+
SecretsGroup.objects.create(name="Secrets Group 2"),
|
|
2202
|
+
)
|
|
2203
|
+
secrets_groups[0].secrets.set([secrets[0]])
|
|
2204
|
+
secrets_groups[1].secrets.set([secrets[1]])
|
|
2205
|
+
|
|
2173
2206
|
def test_provider(self):
|
|
2174
2207
|
params = {"provider": ["environment-variable"]}
|
|
2175
2208
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
@@ -27,6 +27,7 @@ from nautobot.core.testing import TestCase
|
|
|
27
27
|
from nautobot.core.testing.models import ModelTestCases
|
|
28
28
|
from nautobot.dcim.models import (
|
|
29
29
|
Device,
|
|
30
|
+
DeviceFamily,
|
|
30
31
|
DeviceType,
|
|
31
32
|
Location,
|
|
32
33
|
LocationType,
|
|
@@ -677,8 +678,11 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
|
|
|
677
678
|
|
|
678
679
|
@classmethod
|
|
679
680
|
def setUpTestData(cls):
|
|
680
|
-
manufacturer = Manufacturer.objects.first()
|
|
681
|
-
cls.
|
|
681
|
+
cls.manufacturer = Manufacturer.objects.first()
|
|
682
|
+
cls.devicefamily = DeviceFamily.objects.create(name="Device Family 1")
|
|
683
|
+
cls.devicetype = DeviceType.objects.create(
|
|
684
|
+
manufacturer=cls.manufacturer, model="Device Type 1", device_family=cls.devicefamily
|
|
685
|
+
)
|
|
682
686
|
cls.devicerole = Role.objects.get_for_model(Device).first()
|
|
683
687
|
root_location_type = LocationType.objects.create(name="Root Location Type")
|
|
684
688
|
parent_location_type = LocationType.objects.create(name="Parent Location Type", parent=root_location_type)
|
|
@@ -1217,6 +1221,38 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
|
|
|
1217
1221
|
self.assertNotIn("cluster_group_2", context.keys())
|
|
1218
1222
|
self.assertNotIn("cluster_group_12", context.keys())
|
|
1219
1223
|
|
|
1224
|
+
def test_device_family_context(self):
|
|
1225
|
+
"""
|
|
1226
|
+
A config context assigned to the device's DeviceFamily is included in get_config_context().
|
|
1227
|
+
"""
|
|
1228
|
+
|
|
1229
|
+
# Create a Family-level context
|
|
1230
|
+
cc_family = ConfigContext.objects.create(
|
|
1231
|
+
name="Device Family 1",
|
|
1232
|
+
weight=100,
|
|
1233
|
+
data={
|
|
1234
|
+
"device_family": "Device Family 1",
|
|
1235
|
+
},
|
|
1236
|
+
)
|
|
1237
|
+
cc_family.device_families.add(self.devicefamily)
|
|
1238
|
+
ctx1 = self.device.get_config_context()
|
|
1239
|
+
|
|
1240
|
+
# Create a second device for a negative test and verify that it does NOT receive the family context
|
|
1241
|
+
device_type2 = DeviceType.objects.create(manufacturer=self.manufacturer, model="Device Type2")
|
|
1242
|
+
device2 = Device.objects.create(
|
|
1243
|
+
name="Device 2",
|
|
1244
|
+
location=self.location,
|
|
1245
|
+
tenant=self.tenant,
|
|
1246
|
+
platform=self.platform,
|
|
1247
|
+
role=self.devicerole,
|
|
1248
|
+
status=self.device_status,
|
|
1249
|
+
device_type=device_type2,
|
|
1250
|
+
)
|
|
1251
|
+
ctx2 = device2.get_config_context()
|
|
1252
|
+
|
|
1253
|
+
self.assertEqual(ctx1.get("device_family"), "Device Family 1")
|
|
1254
|
+
self.assertEqual(ctx2.get("device_family"), None)
|
|
1255
|
+
|
|
1220
1256
|
|
|
1221
1257
|
class ConfigContextSchemaTestCase(ModelTestCases.BaseModelTestCase):
|
|
1222
1258
|
"""
|
|
@@ -4,7 +4,7 @@ import uuid
|
|
|
4
4
|
from django.core.cache import cache
|
|
5
5
|
|
|
6
6
|
from nautobot.core.testing import TestCase
|
|
7
|
-
from nautobot.dcim.models import Device,
|
|
7
|
+
from nautobot.dcim.models import Cable, Device, PowerPort
|
|
8
8
|
from nautobot.extras.choices import JobQueueTypeChoices
|
|
9
9
|
from nautobot.extras.models import JobQueue
|
|
10
10
|
from nautobot.extras.registry import registry
|
|
@@ -23,11 +23,10 @@ class UtilsTestCase(TestCase):
|
|
|
23
23
|
self.assertEqual(get_base_template("dcim/device/base.html", Device), "dcim/device/base.html")
|
|
24
24
|
|
|
25
25
|
with self.subTest("<model>.html wins over <model>_retrieve.html"):
|
|
26
|
-
|
|
27
|
-
self.assertEqual(get_base_template(None, LocationType), "dcim/locationtype.html")
|
|
26
|
+
self.assertEqual(get_base_template(None, PowerPort), "dcim/powerport.html")
|
|
28
27
|
|
|
29
28
|
with self.subTest("<model>_retrieve.html is used if present"):
|
|
30
|
-
self.assertEqual(get_base_template(None,
|
|
29
|
+
self.assertEqual(get_base_template(None, Cable), "dcim/cable_retrieve.html")
|
|
31
30
|
|
|
32
31
|
with self.subTest("generic/object_retrieve.html is used as a fallback"):
|
|
33
32
|
self.assertEqual(get_base_template(None, Token), "generic/object_retrieve.html")
|