nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__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.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/choices.py +0 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +9 -1
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit.html +1 -1
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination.html +1 -1
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/circuittype.html +1 -1
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/templates/circuits/providernetwork.html +1 -1
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- nautobot/cloud/filters.py +1 -1
- nautobot/cloud/navigation.py +3 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +6 -1
- nautobot/core/api/urls.py +1 -0
- nautobot/core/api/views.py +8 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
- nautobot/core/cli/migrate_deprecated_templates.py +200 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/jobs/__init__.py +6 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/settings.py +18 -12
- nautobot/core/settings.yaml +13 -7
- nautobot/core/signals.py +12 -1
- nautobot/core/tables.py +13 -6
- nautobot/core/templates/40x.html +1 -1
- nautobot/core/templates/500.html +2 -2
- nautobot/core/templates/admin/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- nautobot/core/templates/buttons/export.html +1 -1
- nautobot/core/templates/components/button/dropdown.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +2 -3
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +1 -1
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +4 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- nautobot/core/templates/generic/object_bulk_remove.html +2 -2
- nautobot/core/templates/generic/object_bulk_update.html +5 -4
- nautobot/core/templates/generic/object_create.html +5 -4
- nautobot/core/templates/generic/object_import.html +2 -1
- nautobot/core/templates/generic/object_list.html +12 -4
- nautobot/core/templates/generic/object_notes.html +5 -3
- nautobot/core/templates/generic/object_retrieve.html +2 -3
- nautobot/core/templates/graphene/graphiql.html +7 -7
- nautobot/core/templates/home.html +1 -1
- nautobot/core/templates/import_success.html +2 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/created_updated.html +7 -3
- nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +1 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/nav_menu.html +2 -1
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -5
- nautobot/core/templates/redoc_ui.html +7 -0
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +17 -3
- nautobot/core/templates/system_jobs/import_objects.html +1 -2
- nautobot/core/templates/utilities/confirmation_form.html +2 -2
- nautobot/core/templates/utilities/obj_table.html +10 -2
- nautobot/core/templates/utilities/render_field.html +7 -7
- nautobot/core/templates/utilities/render_jinja2.html +2 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +4 -4
- nautobot/core/templates/utilities/theme_preview.html +16 -3
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +52 -6
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +23 -10
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/views.py +4 -0
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +9 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +6 -7
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_cli.py +40 -0
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_forms.py +55 -13
- nautobot/core/tests/test_jobs.py +75 -1
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +76 -1
- nautobot/core/tests/test_patch_social_django.py +42 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +53 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +1 -1
- nautobot/core/tests/test_utils.py +31 -4
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +2 -12
- nautobot/core/ui/choices.py +142 -10
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/object_detail.py +92 -12
- nautobot/core/urls.py +12 -1
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +17 -17
- nautobot/core/utils/lookup.py +3 -8
- nautobot/core/utils/module_loading.py +21 -0
- nautobot/core/utils/patch_social_django.py +128 -0
- nautobot/core/views/__init__.py +38 -1
- nautobot/core/views/generic.py +3 -3
- nautobot/core/views/mixins.py +15 -3
- nautobot/core/views/renderers.py +2 -0
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/apps.py +1 -5
- nautobot/data_validation/custom_validators.py +4 -4
- nautobot/data_validation/filters.py +1 -1
- nautobot/data_validation/forms.py +40 -0
- nautobot/data_validation/migrations/0001_initial.py +0 -7
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +0 -12
- nautobot/data_validation/models.py +16 -7
- nautobot/data_validation/navigation.py +8 -1
- nautobot/data_validation/tables.py +12 -5
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +2 -2
- nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
- nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
- nautobot/data_validation/tests/test_filters.py +8 -6
- nautobot/data_validation/tests/test_models.py +15 -0
- nautobot/data_validation/tests/test_views.py +190 -32
- nautobot/data_validation/urls.py +2 -5
- nautobot/data_validation/views.py +73 -40
- nautobot/dcim/api/serializers.py +0 -13
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +16 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +2 -35
- nautobot/dcim/forms.py +1 -1
- nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
- nautobot/dcim/models/device_components.py +81 -68
- nautobot/dcim/models/devices.py +13 -16
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +3 -0
- nautobot/dcim/tables/template_code.py +14 -14
- nautobot/dcim/templates/dcim/cable.html +2 -61
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_edit.html +2 -5
- nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
- nautobot/dcim/templates/dcim/cable_trace.html +1 -3
- nautobot/dcim/templates/dcim/cable_update.html +5 -0
- nautobot/dcim/templates/dcim/consoleport.html +6 -5
- nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
- nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
- nautobot/dcim/templates/dcim/device/frontports.html +1 -1
- nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
- nautobot/dcim/templates/dcim/device/inventory.html +1 -1
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
- nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/device/powerports.html +1 -1
- nautobot/dcim/templates/dcim/device/rearports.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device/wireless.html +1 -1
- nautobot/dcim/templates/dcim/device.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +2 -2
- nautobot/dcim/templates/dcim/device_create.html +5 -3
- nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
- nautobot/dcim/templates/dcim/device_list.html +73 -10
- nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
- nautobot/dcim/templates/dcim/frontport.html +9 -8
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +26 -6
- nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -242
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +5 -4
- nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
- nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
- nautobot/dcim/templates/dcim/rearport.html +7 -6
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
- nautobot/dcim/tests/integration/test_controller.py +1 -0
- nautobot/dcim/tests/test_api.py +8 -0
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +12 -6
- nautobot/dcim/tests/test_models.py +63 -4
- nautobot/dcim/tests/test_views.py +63 -22
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +547 -273
- nautobot/extras/api/views.py +9 -1
- nautobot/extras/choices.py +2 -13
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
- nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
- nautobot/extras/{filters/__init__.py → filters.py} +14 -46
- nautobot/extras/forms/forms.py +5 -13
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/management/__init__.py +9 -0
- nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +22 -13
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +59 -1
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +13 -0
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/navigation.py +13 -9
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -4
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +52 -68
- nautobot/extras/templates/extras/approval_dashboard.html +7 -5
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
- nautobot/extras/templates/extras/computedfield.html +1 -1
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/customfield.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -96
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
- nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
- nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
- nautobot/extras/templates/extras/inc/json_format.html +10 -3
- nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
- nautobot/extras/templates/extras/job.html +29 -12
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
- nautobot/extras/templates/extras/job_edit.html +52 -46
- nautobot/extras/templates/extras/job_list.html +29 -25
- nautobot/extras/templates/extras/marketplace.html +5 -9
- nautobot/extras/templates/extras/object_configcontext.html +1 -1
- nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
- nautobot/extras/templates/extras/objectchange_retrieve.html +19 -37
- nautobot/extras/templates/extras/plugin_detail.html +26 -21
- nautobot/extras/templates/extras/plugins_list.html +16 -26
- nautobot/extras/templates/extras/role_retrieve.html +64 -0
- nautobot/extras/templates/extras/scheduledjob.html +4 -2
- nautobot/extras/templates/extras/secretsgroup.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +8 -7
- nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/test_api.py +27 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +35 -3
- nautobot/extras/tests/test_customfields.py +22 -13
- nautobot/extras/tests/test_customfields_filters.py +479 -0
- nautobot/extras/tests/test_dynamicgroups.py +39 -1
- nautobot/extras/tests/test_filters.py +21 -19
- nautobot/extras/tests/test_forms.py +18 -21
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +1 -0
- nautobot/extras/tests/test_models.py +13 -31
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_views.py +31 -30
- nautobot/extras/views.py +81 -19
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +27 -38
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -2
- nautobot/ipam/signals.py +71 -0
- nautobot/ipam/tables.py +13 -6
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
- nautobot/ipam/templates/ipam/ipaddress.html +14 -0
- nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/service.html +1 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +13 -6
- nautobot/ipam/tests/test_filters.py +10 -0
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +43 -1
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +13 -14
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +46 -6
- nautobot/project-static/dist/css/nautobot.css +1 -1
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +1 -1
- nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
- nautobot/project-static/nautobot-icons/elements.svg +1 -1
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +1 -1
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +1 -1
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +1 -1
- nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
- nautobot/project-static/nautobot-icons/star.svg +1 -1
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -0
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +61 -28
- nautobot/users/templates/users/profile.html +45 -12
- nautobot/users/templates/users/sessionkey_delete.html +1 -1
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/views.py +4 -2
- nautobot/virtualization/models.py +1 -68
- nautobot/virtualization/navigation.py +3 -2
- nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_models.py +44 -4
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +209 -0
- nautobot/vpn/filters.py +233 -0
- nautobot/vpn/forms.py +486 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +527 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +380 -0
- nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +341 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +294 -0
- nautobot/vpn/tests/test_models.py +97 -0
- nautobot/vpn/tests/test_views.py +281 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +437 -0
- nautobot/wireless/navigation.py +3 -2
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +14 -14
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
- nautobot/data_validation/template_content.py +0 -42
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
|
@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
|
|
|
6
6
|
from django.contrib.auth.models import Group
|
|
7
7
|
from django.contrib.contenttypes.models import ContentType
|
|
8
8
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
9
|
-
from django.test import override_settings, RequestFactory
|
|
9
|
+
from django.test import override_settings, RequestFactory, tag
|
|
10
10
|
from django.utils.timezone import now
|
|
11
11
|
|
|
12
12
|
from nautobot.core.jobs import BulkDeleteObjects
|
|
@@ -165,35 +165,35 @@ class ApprovalWorkflowTestMixin:
|
|
|
165
165
|
cls.approval_workflow_1_definition = ApprovalWorkflowDefinition.objects.create(
|
|
166
166
|
name="Test Approval Workflow 1 Definition",
|
|
167
167
|
model_content_type=cls.scheduledjob_ct,
|
|
168
|
-
|
|
168
|
+
weight=1,
|
|
169
169
|
)
|
|
170
170
|
cls.approval_workflow_2_definition = ApprovalWorkflowDefinition.objects.create(
|
|
171
171
|
name="Test Approval Workflow 2 Definition",
|
|
172
172
|
model_content_type=cls.scheduledjob_ct,
|
|
173
173
|
model_constraints={"name": "Bulk Delete Objects"},
|
|
174
|
-
|
|
174
|
+
weight=2,
|
|
175
175
|
)
|
|
176
176
|
cls.approval_workflow_3_definition = ApprovalWorkflowDefinition.objects.create(
|
|
177
177
|
name="Test Approval Workflow 3 Definition",
|
|
178
178
|
model_content_type=cls.scheduledjob_ct,
|
|
179
179
|
model_constraints={"name": "Bulk Delete Objects"},
|
|
180
|
-
|
|
180
|
+
weight=3,
|
|
181
181
|
)
|
|
182
182
|
cls.approval_workflow_4_definition = ApprovalWorkflowDefinition.objects.create(
|
|
183
183
|
name="Test Approval Workflow 4 Definition",
|
|
184
184
|
model_content_type=cls.scheduledjob_ct,
|
|
185
185
|
model_constraints={"name": "Bulk Delete Objects"},
|
|
186
|
-
|
|
186
|
+
weight=4,
|
|
187
187
|
)
|
|
188
188
|
cls.approval_workflow_5_definition = ApprovalWorkflowDefinition.objects.create(
|
|
189
189
|
name="Test Approval Workflow 5 Definition",
|
|
190
190
|
model_content_type=cls.scheduledjob_ct,
|
|
191
191
|
model_constraints={"name": "Bulk Delete Objects"},
|
|
192
|
-
|
|
192
|
+
weight=5,
|
|
193
193
|
)
|
|
194
194
|
cls.approval_workflow_1_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
195
195
|
approval_workflow_definition=cls.approval_workflow_1_definition,
|
|
196
|
-
|
|
196
|
+
sequence=100,
|
|
197
197
|
name="Test Approval Workflow 1 Stage 1 Definition",
|
|
198
198
|
min_approvers=2,
|
|
199
199
|
denial_message="Stage 1 Denial Message",
|
|
@@ -201,7 +201,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
201
201
|
)
|
|
202
202
|
cls.approval_workflow_1_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
203
203
|
approval_workflow_definition=cls.approval_workflow_1_definition,
|
|
204
|
-
|
|
204
|
+
sequence=200,
|
|
205
205
|
name="Test Approval Workflow 1 Stage 2 Definition",
|
|
206
206
|
min_approvers=2,
|
|
207
207
|
denial_message="Stage 2 Denial Message",
|
|
@@ -209,7 +209,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
209
209
|
)
|
|
210
210
|
cls.approval_workflow_1_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
211
211
|
approval_workflow_definition=cls.approval_workflow_1_definition,
|
|
212
|
-
|
|
212
|
+
sequence=300,
|
|
213
213
|
name="Test Approval Workflow 1 Stage 3 Definition",
|
|
214
214
|
min_approvers=3,
|
|
215
215
|
denial_message="Stage 3 Denial Message",
|
|
@@ -217,7 +217,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
217
217
|
)
|
|
218
218
|
cls.approval_workflow_1_stage_4_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
219
219
|
approval_workflow_definition=cls.approval_workflow_1_definition,
|
|
220
|
-
|
|
220
|
+
sequence=400,
|
|
221
221
|
name="Test Approval Workflow 1 Stage 4 Definition",
|
|
222
222
|
min_approvers=5,
|
|
223
223
|
denial_message="Stage 4 Denial Message",
|
|
@@ -225,7 +225,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
225
225
|
)
|
|
226
226
|
cls.approval_workflow_1_stage_5_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
227
227
|
approval_workflow_definition=cls.approval_workflow_1_definition,
|
|
228
|
-
|
|
228
|
+
sequence=500,
|
|
229
229
|
name="Test Approval Workflow 1 Stage 5 Definition",
|
|
230
230
|
min_approvers=2,
|
|
231
231
|
denial_message="Stage 5 Denial Message",
|
|
@@ -233,7 +233,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
233
233
|
)
|
|
234
234
|
cls.approval_workflow_1_stage_6_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
235
235
|
approval_workflow_definition=cls.approval_workflow_1_definition,
|
|
236
|
-
|
|
236
|
+
sequence=600,
|
|
237
237
|
name="Test Approval Workflow 1 Stage 6 Definition",
|
|
238
238
|
min_approvers=2,
|
|
239
239
|
denial_message="Stage 6 Denial Message",
|
|
@@ -241,7 +241,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
241
241
|
)
|
|
242
242
|
cls.approval_workflow_2_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
243
243
|
approval_workflow_definition=cls.approval_workflow_2_definition,
|
|
244
|
-
|
|
244
|
+
sequence=100,
|
|
245
245
|
name="Test Approval Workflow 2 Stage 1 Definition",
|
|
246
246
|
min_approvers=2,
|
|
247
247
|
denial_message="Stage 1 Denial Message",
|
|
@@ -249,7 +249,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
249
249
|
)
|
|
250
250
|
cls.approval_workflow_2_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
251
251
|
approval_workflow_definition=cls.approval_workflow_2_definition,
|
|
252
|
-
|
|
252
|
+
sequence=200,
|
|
253
253
|
name="Test Approval Workflow 2 Stage 2 Definition",
|
|
254
254
|
min_approvers=2,
|
|
255
255
|
denial_message="Stage 2 Denial Message",
|
|
@@ -257,7 +257,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
257
257
|
)
|
|
258
258
|
cls.approval_workflow_2_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
259
259
|
approval_workflow_definition=cls.approval_workflow_2_definition,
|
|
260
|
-
|
|
260
|
+
sequence=300,
|
|
261
261
|
name="Test Approval Workflow 2 Stage 3 Definition",
|
|
262
262
|
min_approvers=2,
|
|
263
263
|
denial_message="Stage 3 Denial Message",
|
|
@@ -265,7 +265,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
265
265
|
)
|
|
266
266
|
cls.approval_workflow_3_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
267
267
|
approval_workflow_definition=cls.approval_workflow_3_definition,
|
|
268
|
-
|
|
268
|
+
sequence=100,
|
|
269
269
|
name="Test Approval Workflow 3 Stage 1 Definition",
|
|
270
270
|
min_approvers=2,
|
|
271
271
|
denial_message="Stage 1 Denial Message",
|
|
@@ -273,7 +273,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
273
273
|
)
|
|
274
274
|
cls.approval_workflow_3_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
275
275
|
approval_workflow_definition=cls.approval_workflow_3_definition,
|
|
276
|
-
|
|
276
|
+
sequence=200,
|
|
277
277
|
name="Test Approval Workflow 3 Stage 2 Definition",
|
|
278
278
|
min_approvers=2,
|
|
279
279
|
denial_message="Stage 2 Denial Message",
|
|
@@ -281,7 +281,7 @@ class ApprovalWorkflowTestMixin:
|
|
|
281
281
|
)
|
|
282
282
|
cls.approval_workflow_3_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
|
|
283
283
|
approval_workflow_definition=cls.approval_workflow_3_definition,
|
|
284
|
-
|
|
284
|
+
sequence=300,
|
|
285
285
|
name="Test Approval Workflow 3 Stage 3 Definition",
|
|
286
286
|
min_approvers=2,
|
|
287
287
|
denial_message="Stage 3 Denial Message",
|
|
@@ -442,7 +442,7 @@ class ApprovalWorkflowStageDefinitionFilterTestCase(ApprovalWorkflowTestMixin, F
|
|
|
442
442
|
("created",),
|
|
443
443
|
("last_updated",),
|
|
444
444
|
("approval_workflow_definition",),
|
|
445
|
-
("
|
|
445
|
+
("sequence",),
|
|
446
446
|
("name",),
|
|
447
447
|
("min_approvers",),
|
|
448
448
|
("denial_message",),
|
|
@@ -1331,6 +1331,7 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
1331
1331
|
params = {"dryrun_default": True}
|
|
1332
1332
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
|
|
1333
1333
|
|
|
1334
|
+
@tag("example_app")
|
|
1334
1335
|
def test_hidden(self):
|
|
1335
1336
|
params = {"hidden": True}
|
|
1336
1337
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
|
@@ -1341,6 +1342,7 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
1341
1342
|
self.filterset(params, self.queryset).qs, self.queryset.filter(read_only=True)
|
|
1342
1343
|
)
|
|
1343
1344
|
|
|
1345
|
+
@tag("example_app")
|
|
1344
1346
|
def test_is_job_hook_receiver(self):
|
|
1345
1347
|
params = {"is_job_hook_receiver": True}
|
|
1346
1348
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from unittest import skip
|
|
2
3
|
import warnings
|
|
3
4
|
|
|
4
5
|
from django.contrib.auth import get_user_model
|
|
@@ -14,14 +15,9 @@ from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoi
|
|
|
14
15
|
from nautobot.extras.forms import (
|
|
15
16
|
ConfigContextFilterForm,
|
|
16
17
|
ConfigContextForm,
|
|
17
|
-
CustomFieldModelBulkEditFormMixin,
|
|
18
18
|
CustomFieldModelFormMixin,
|
|
19
19
|
JobButtonForm,
|
|
20
20
|
JobHookForm,
|
|
21
|
-
RelationshipModelFormMixin,
|
|
22
|
-
StatusModelBulkEditFormMixin,
|
|
23
|
-
StatusModelFilterFormMixin,
|
|
24
|
-
TagsBulkEditFormMixin,
|
|
25
21
|
WebhookForm,
|
|
26
22
|
)
|
|
27
23
|
from nautobot.extras.models import (
|
|
@@ -1095,29 +1091,30 @@ class WebhookFormTestCase(TestCase):
|
|
|
1095
1091
|
)
|
|
1096
1092
|
|
|
1097
1093
|
|
|
1094
|
+
@skip(reason="Skipping until we have items that need to be deprecated again.")
|
|
1098
1095
|
class DeprecatedAliasesTestCase(TestCase):
|
|
1099
1096
|
"""Test that deprecated class names still exist, but report a DeprecationWarning when used."""
|
|
1100
1097
|
|
|
1101
1098
|
def test_deprecated_form_mixin_classes(self):
|
|
1102
1099
|
# Importing these mixin classes doesn't directly warn, but subclassing them does.
|
|
1103
|
-
from nautobot.extras.forms import CustomFieldBulkCreateForm
|
|
1104
|
-
from nautobot.extras.forms.mixins import (
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
)
|
|
1100
|
+
# from nautobot.extras.forms import CustomFieldBulkCreateForm
|
|
1101
|
+
# from nautobot.extras.forms.mixins import (
|
|
1102
|
+
# AddRemoveTagsForm,
|
|
1103
|
+
# CustomFieldBulkEditForm,
|
|
1104
|
+
# CustomFieldModelForm,
|
|
1105
|
+
# RelationshipModelForm,
|
|
1106
|
+
# StatusBulkEditFormMixin,
|
|
1107
|
+
# StatusFilterFormMixin,
|
|
1108
|
+
# )
|
|
1112
1109
|
|
|
1113
1110
|
for deprecated_form_class, replacement_form_class in (
|
|
1114
|
-
(AddRemoveTagsForm, TagsBulkEditFormMixin),
|
|
1115
|
-
(CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
|
|
1116
|
-
(CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
|
|
1117
|
-
(CustomFieldModelForm, CustomFieldModelFormMixin),
|
|
1118
|
-
(RelationshipModelForm, RelationshipModelFormMixin),
|
|
1119
|
-
(StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
|
|
1120
|
-
(StatusFilterFormMixin, StatusModelFilterFormMixin),
|
|
1111
|
+
# (AddRemoveTagsForm, TagsBulkEditFormMixin),
|
|
1112
|
+
# (CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
|
|
1113
|
+
# (CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
|
|
1114
|
+
# (CustomFieldModelForm, CustomFieldModelFormMixin),
|
|
1115
|
+
# (RelationshipModelForm, RelationshipModelFormMixin),
|
|
1116
|
+
# (StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
|
|
1117
|
+
# (StatusFilterFormMixin, StatusModelFilterFormMixin),
|
|
1121
1118
|
):
|
|
1122
1119
|
with self.subTest(msg=f"Replace {deprecated_form_class.__name__} with {replacement_form_class.__name__}"):
|
|
1123
1120
|
# Subclassing the deprecated class should raise a DeprecationWarning
|
|
@@ -16,7 +16,7 @@ from django.core.exceptions import ValidationError
|
|
|
16
16
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
17
17
|
from django.core.management import call_command
|
|
18
18
|
from django.core.management.base import CommandError
|
|
19
|
-
from django.test import override_settings
|
|
19
|
+
from django.test import override_settings, tag
|
|
20
20
|
from django.test.client import RequestFactory
|
|
21
21
|
from django.utils import timezone
|
|
22
22
|
|
|
@@ -286,6 +286,7 @@ register_jobs(BadJob)
|
|
|
286
286
|
# Clean up back to normal behavior
|
|
287
287
|
get_jobs(reload=True)
|
|
288
288
|
|
|
289
|
+
@tag("example_app")
|
|
289
290
|
def test_app_dynamic_jobs(self):
|
|
290
291
|
"""
|
|
291
292
|
Test that get_job() correctly reloads dynamic jobs when `NautobotAppConfig.provides_dynamic_jobs` is True.
|
|
@@ -645,6 +646,26 @@ class JobTransactionTest(TransactionTestCase):
|
|
|
645
646
|
self.assertFalse(logs.filter(message="I should NOT be logged to the database").exists())
|
|
646
647
|
self.assertTrue(logs.filter(message="I should be logged to the database").exists())
|
|
647
648
|
|
|
649
|
+
def test_log_counts_by_level(self):
|
|
650
|
+
"""
|
|
651
|
+
Test that related JobLogEntry counts are stored for JobResult list summary.
|
|
652
|
+
"""
|
|
653
|
+
module = "log_counts_by_level"
|
|
654
|
+
name = "TestLogCountsByLevel"
|
|
655
|
+
job_result = create_job_result_and_run_job(module, name)
|
|
656
|
+
|
|
657
|
+
self.assertGreater(job_result.job_log_entries.count(), 0)
|
|
658
|
+
self.assertIsNotNone(job_result.debug_log_count)
|
|
659
|
+
self.assertEqual(job_result.debug_log_count, 0)
|
|
660
|
+
self.assertIsNotNone(job_result.success_log_count)
|
|
661
|
+
self.assertEqual(job_result.success_log_count, 1)
|
|
662
|
+
self.assertIsNotNone(job_result.info_log_count)
|
|
663
|
+
self.assertEqual(job_result.info_log_count, 3)
|
|
664
|
+
self.assertIsNotNone(job_result.warning_log_count)
|
|
665
|
+
self.assertEqual(job_result.warning_log_count, 2)
|
|
666
|
+
self.assertIsNotNone(job_result.error_log_count)
|
|
667
|
+
self.assertEqual(job_result.error_log_count, 3)
|
|
668
|
+
|
|
648
669
|
def test_object_vars(self):
|
|
649
670
|
"""
|
|
650
671
|
Test that Object variable fields behave as expected.
|
|
@@ -1261,10 +1282,10 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
|
|
|
1261
1282
|
status = models.Status.objects.get_for_model(Location).first()
|
|
1262
1283
|
loc = Location.objects.create(name="Test Job Hook Location 1", location_type=self.location_type, status=status)
|
|
1263
1284
|
models.ObjectChange.objects.all().delete()
|
|
1264
|
-
|
|
1265
|
-
|
|
1285
|
+
tag_instance = models.Tag.objects.create(name="A Test Tag")
|
|
1286
|
+
tag_instance.content_types.add(ContentType.objects.get_for_model(Location))
|
|
1266
1287
|
with web_request_context(user=self.user):
|
|
1267
|
-
loc.tags.add(
|
|
1288
|
+
loc.tags.add(tag_instance)
|
|
1268
1289
|
job_result = models.JobResult.objects.filter(job_model=self.job_model).first()
|
|
1269
1290
|
self.assertIsNotNone(job_result)
|
|
1270
1291
|
expected_log_messages = [
|
|
@@ -118,6 +118,7 @@ class CustomFieldDataMigrationTest(NautobotDataMigrationTest):
|
|
|
118
118
|
self.assertEqual(cf_1.key, "a123_main_ave")
|
|
119
119
|
self.assertEqual(cf_2.key, "a_456_main_ave")
|
|
120
120
|
|
|
121
|
+
@tag("example_app")
|
|
121
122
|
@skip("Something bad is happening with the test data, suspecting bad merge")
|
|
122
123
|
def test_custom_field_data_populated_correctly(self):
|
|
123
124
|
location_0 = self.location.objects.get(name="Test Location 1")
|
|
@@ -4,7 +4,6 @@ import shutil
|
|
|
4
4
|
import tempfile
|
|
5
5
|
from unittest import expectedFailure, mock
|
|
6
6
|
import uuid
|
|
7
|
-
import warnings
|
|
8
7
|
from zoneinfo import ZoneInfo
|
|
9
8
|
|
|
10
9
|
from django.conf import settings
|
|
@@ -15,8 +14,7 @@ from django.core.exceptions import ValidationError
|
|
|
15
14
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
16
15
|
from django.db.models import ProtectedError
|
|
17
16
|
from django.db.utils import IntegrityError
|
|
18
|
-
from django.test import override_settings
|
|
19
|
-
from django.test.utils import isolate_apps
|
|
17
|
+
from django.test import override_settings, tag
|
|
20
18
|
from django.utils.timezone import get_default_timezone, now
|
|
21
19
|
from django_celery_beat.tzcrontab import TzAwareCrontab
|
|
22
20
|
from git import GitCommandError
|
|
@@ -90,7 +88,6 @@ from nautobot.extras.models import (
|
|
|
90
88
|
Team,
|
|
91
89
|
Webhook,
|
|
92
90
|
)
|
|
93
|
-
from nautobot.extras.models.statuses import StatusModel
|
|
94
91
|
from nautobot.extras.registry import registry
|
|
95
92
|
from nautobot.extras.secrets.exceptions import SecretParametersError, SecretProviderError, SecretValueNotFoundError
|
|
96
93
|
from nautobot.extras.tests.git_helper import create_and_populate_git_repository
|
|
@@ -103,8 +100,6 @@ from nautobot.virtualization.models import (
|
|
|
103
100
|
VirtualMachine,
|
|
104
101
|
)
|
|
105
102
|
|
|
106
|
-
from example_app.jobs import ExampleJob
|
|
107
|
-
|
|
108
103
|
User = get_user_model()
|
|
109
104
|
|
|
110
105
|
|
|
@@ -146,12 +141,12 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
|
|
|
146
141
|
cls.approval_workflow_definition = ApprovalWorkflowDefinition.objects.create(
|
|
147
142
|
name="Approval Workflow with Three Stages",
|
|
148
143
|
model_content_type=cls.scheduledjob_ct,
|
|
149
|
-
|
|
144
|
+
weight=1,
|
|
150
145
|
)
|
|
151
146
|
# Create three stages of the Approval Workflow Definition
|
|
152
147
|
cls.approval_workflow_stage_definition_1 = ApprovalWorkflowStageDefinition.objects.create(
|
|
153
148
|
approval_workflow_definition=cls.approval_workflow_definition,
|
|
154
|
-
|
|
149
|
+
sequence=100,
|
|
155
150
|
name="Approval Workflow Stage Definition 1",
|
|
156
151
|
min_approvers=1,
|
|
157
152
|
denial_message="Stage 1 Denial Message",
|
|
@@ -159,7 +154,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
|
|
|
159
154
|
)
|
|
160
155
|
cls.approval_workflow_stage_definition_2 = ApprovalWorkflowStageDefinition.objects.create(
|
|
161
156
|
approval_workflow_definition=cls.approval_workflow_definition,
|
|
162
|
-
|
|
157
|
+
sequence=200,
|
|
163
158
|
name="Approval Workflow Stage Definition 2",
|
|
164
159
|
min_approvers=2,
|
|
165
160
|
denial_message="Stage 2 Denial Message",
|
|
@@ -167,7 +162,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
|
|
|
167
162
|
)
|
|
168
163
|
cls.approval_workflow_stage_definition_3 = ApprovalWorkflowStageDefinition.objects.create(
|
|
169
164
|
approval_workflow_definition=cls.approval_workflow_definition,
|
|
170
|
-
|
|
165
|
+
sequence=300,
|
|
171
166
|
name="Approval Workflow Stage Definition 3",
|
|
172
167
|
min_approvers=2,
|
|
173
168
|
denial_message="Stage 3 Denial Message",
|
|
@@ -228,7 +223,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
|
|
|
228
223
|
"""
|
|
229
224
|
Test the active_stage property of the ApprovalWorkflow model.
|
|
230
225
|
"""
|
|
231
|
-
# Test that the active stage is the one with the lowest
|
|
226
|
+
# Test that the active stage is the one with the lowest sequence when all stages are pending
|
|
232
227
|
self.assertEqual(self.approval_workflow.active_stage, self.approval_workflow_stage_1)
|
|
233
228
|
# Test that the active stage is the second stage when the first stage is approved
|
|
234
229
|
self.approval_workflow_stage_1_response_1.state = ApprovalWorkflowStateChoices.APPROVED
|
|
@@ -323,7 +318,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
|
|
|
323
318
|
approval_workflow_definition = ApprovalWorkflowDefinition.objects.create(
|
|
324
319
|
name="Scheduled Job Approval Workflow",
|
|
325
320
|
model_content_type=scheduled_job_ct,
|
|
326
|
-
|
|
321
|
+
weight=2,
|
|
327
322
|
)
|
|
328
323
|
approval_workflow = ApprovalWorkflow(
|
|
329
324
|
approval_workflow_definition=approval_workflow_definition,
|
|
@@ -1961,6 +1956,7 @@ class GitRepositoryTest(ModelTestCases.BaseModelTestCase):
|
|
|
1961
1956
|
shutil.rmtree(self.tempdir.name, ignore_errors=True)
|
|
1962
1957
|
|
|
1963
1958
|
|
|
1959
|
+
@tag("example_app")
|
|
1964
1960
|
class JobModelTest(ModelTestCases.BaseModelTestCase):
|
|
1965
1961
|
"""
|
|
1966
1962
|
Tests for the `Job` model class.
|
|
@@ -1976,6 +1972,8 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
|
|
|
1976
1972
|
cls.app_job = JobModel.objects.get(job_class_name="ExampleJob")
|
|
1977
1973
|
|
|
1978
1974
|
def test_job_class(self):
|
|
1975
|
+
from example_app.jobs import ExampleJob
|
|
1976
|
+
|
|
1979
1977
|
self.assertIsNotNone(self.local_job.job_class)
|
|
1980
1978
|
self.assertEqual(self.local_job.job_class.description, "Validate job import")
|
|
1981
1979
|
|
|
@@ -3495,31 +3493,15 @@ class StatusTest(ModelTestCases.BaseModelTestCase):
|
|
|
3495
3493
|
self.status.save()
|
|
3496
3494
|
self.assertEqual(str(self.status), test)
|
|
3497
3495
|
|
|
3498
|
-
@isolate_apps("nautobot.extras.tests")
|
|
3499
|
-
def test_deprecated_mixin_class(self):
|
|
3500
|
-
"""Test that inheriting from StatusModel raises a DeprecationWarning."""
|
|
3501
|
-
with warnings.catch_warnings(record=True) as warn_list:
|
|
3502
|
-
warnings.simplefilter("always")
|
|
3503
|
-
|
|
3504
|
-
class MyModel(StatusModel): # pylint: disable=unused-variable
|
|
3505
|
-
pass
|
|
3506
|
-
|
|
3507
|
-
self.assertEqual(len(warn_list), 1)
|
|
3508
|
-
warning = warn_list[0]
|
|
3509
|
-
self.assertTrue(issubclass(warning.category, DeprecationWarning))
|
|
3510
|
-
self.assertIn("StatusModel is deprecated", str(warning))
|
|
3511
|
-
self.assertIn("Instead of deriving MyModel from StatusModel", str(warning))
|
|
3512
|
-
self.assertIn("please directly declare `status = StatusField(...)` on your model instead", str(warning))
|
|
3513
|
-
|
|
3514
3496
|
|
|
3515
3497
|
class TagTest(ModelTestCases.BaseModelTestCase):
|
|
3516
3498
|
model = Tag
|
|
3517
3499
|
|
|
3518
3500
|
def test_create_tag_unicode(self):
|
|
3519
|
-
|
|
3520
|
-
|
|
3501
|
+
instance = Tag(name="Testing Unicode: 台灣")
|
|
3502
|
+
instance.save()
|
|
3521
3503
|
|
|
3522
|
-
self.assertEqual(
|
|
3504
|
+
self.assertEqual(instance.name, "Testing Unicode: 台灣")
|
|
3523
3505
|
|
|
3524
3506
|
|
|
3525
3507
|
class JobLogEntryTest(TestCase): # TODO: change to BaseModelTestCase
|
|
@@ -5,7 +5,7 @@ from django.conf import settings
|
|
|
5
5
|
from django.contrib.contenttypes.models import ContentType
|
|
6
6
|
from django.core.exceptions import ValidationError
|
|
7
7
|
from django.template import engines
|
|
8
|
-
from django.test import override_settings
|
|
8
|
+
from django.test import override_settings, tag
|
|
9
9
|
from django.urls import NoReverseMatch, reverse
|
|
10
10
|
import django_tables2 as tables
|
|
11
11
|
import netaddr
|
|
@@ -32,11 +32,8 @@ from nautobot.tenancy.forms import TenantFilterForm
|
|
|
32
32
|
from nautobot.tenancy.models import Tenant, TenantGroup
|
|
33
33
|
from nautobot.users.models import ObjectPermission
|
|
34
34
|
|
|
35
|
-
from example_app import config as example_config
|
|
36
|
-
from example_app.datasources import refresh_git_text_files
|
|
37
|
-
from example_app.models import ExampleModel
|
|
38
|
-
|
|
39
35
|
|
|
36
|
+
@tag("example_app")
|
|
40
37
|
class AppTest(TestCase):
|
|
41
38
|
def test_config(self):
|
|
42
39
|
self.assertIn(
|
|
@@ -46,6 +43,8 @@ class AppTest(TestCase):
|
|
|
46
43
|
|
|
47
44
|
def test_models(self):
|
|
48
45
|
# Test saving an instance
|
|
46
|
+
from example_app.models import ExampleModel
|
|
47
|
+
|
|
49
48
|
instance = ExampleModel(name="Instance 1", number=100)
|
|
50
49
|
instance.save()
|
|
51
50
|
self.assertIsNotNone(instance.pk)
|
|
@@ -134,6 +133,8 @@ class AppTest(TestCase):
|
|
|
134
133
|
"""
|
|
135
134
|
Check that App DatasourceContents are registered.
|
|
136
135
|
"""
|
|
136
|
+
from example_app.datasources import refresh_git_text_files
|
|
137
|
+
|
|
137
138
|
registered_datasources = registry.get("datasource_contents", {}).get("extras.gitrepository", [])
|
|
138
139
|
|
|
139
140
|
app_datasource = DatasourceContent(
|
|
@@ -158,6 +159,8 @@ class AppTest(TestCase):
|
|
|
158
159
|
"""
|
|
159
160
|
Check that App middleware is registered.
|
|
160
161
|
"""
|
|
162
|
+
from example_app import config as example_config
|
|
163
|
+
|
|
161
164
|
self.assertIn(
|
|
162
165
|
"example_app.middleware.ExampleMiddleware",
|
|
163
166
|
settings.MIDDLEWARE,
|
|
@@ -175,6 +178,8 @@ class AppTest(TestCase):
|
|
|
175
178
|
"""
|
|
176
179
|
Check enforcement of minimum Nautobot version.
|
|
177
180
|
"""
|
|
181
|
+
from example_app import config as example_config
|
|
182
|
+
|
|
178
183
|
with self.assertRaises(PluginImproperlyConfigured):
|
|
179
184
|
example_config.validate({}, "0.8")
|
|
180
185
|
|
|
@@ -182,6 +187,8 @@ class AppTest(TestCase):
|
|
|
182
187
|
"""
|
|
183
188
|
Check enforcement of maximum Nautobot version.
|
|
184
189
|
"""
|
|
190
|
+
from example_app import config as example_config
|
|
191
|
+
|
|
185
192
|
with self.assertRaises(PluginImproperlyConfigured):
|
|
186
193
|
example_config.validate({}, "10.0")
|
|
187
194
|
|
|
@@ -189,6 +196,7 @@ class AppTest(TestCase):
|
|
|
189
196
|
"""
|
|
190
197
|
Validate enforcement of required settings.
|
|
191
198
|
"""
|
|
199
|
+
from example_app import config as example_config
|
|
192
200
|
|
|
193
201
|
class ExampleConfigWithRequiredSettings(example_config):
|
|
194
202
|
required_settings = ["foo"]
|
|
@@ -210,6 +218,7 @@ class AppTest(TestCase):
|
|
|
210
218
|
"""
|
|
211
219
|
Validate population of default config settings.
|
|
212
220
|
"""
|
|
221
|
+
from example_app import config as example_config
|
|
213
222
|
|
|
214
223
|
class ExampleConfigWithDefaultSettings(example_config):
|
|
215
224
|
default_settings = {
|
|
@@ -239,6 +248,8 @@ class AppTest(TestCase):
|
|
|
239
248
|
"""
|
|
240
249
|
Validate that App installed Django apps and dependencies are are registered.
|
|
241
250
|
"""
|
|
251
|
+
from example_app import config as example_config
|
|
252
|
+
|
|
242
253
|
self.assertIn(
|
|
243
254
|
"example_app.ExampleAppConfig",
|
|
244
255
|
settings.INSTALLED_APPS,
|
|
@@ -283,11 +294,13 @@ class AppTest(TestCase):
|
|
|
283
294
|
self.assertEqual(secret.get_value(obj=secret), secret.parameters["constant"])
|
|
284
295
|
|
|
285
296
|
|
|
297
|
+
@tag("example_app")
|
|
286
298
|
class AppGenericViewTest(ViewTestCases.PrimaryObjectViewTestCase):
|
|
287
|
-
model = ExampleModel
|
|
288
|
-
|
|
289
299
|
@classmethod
|
|
290
300
|
def setUpTestData(cls):
|
|
301
|
+
from example_app.models import ExampleModel
|
|
302
|
+
|
|
303
|
+
cls.model = ExampleModel
|
|
291
304
|
# Example objects to test.
|
|
292
305
|
ExampleModel.objects.create(name="Example 1", number=1)
|
|
293
306
|
ExampleModel.objects.create(name="Example 2", number=2)
|
|
@@ -311,6 +324,7 @@ class AppGenericViewTest(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
311
324
|
}
|
|
312
325
|
|
|
313
326
|
|
|
327
|
+
@tag("example_app")
|
|
314
328
|
class AppListViewTest(TestCase):
|
|
315
329
|
def test_list_plugins_anonymous(self):
|
|
316
330
|
# Make the request as an unauthenticated user
|
|
@@ -382,6 +396,7 @@ class AppListViewTest(TestCase):
|
|
|
382
396
|
)
|
|
383
397
|
|
|
384
398
|
|
|
399
|
+
@tag("example_app")
|
|
385
400
|
class PluginDetailViewTest(TestCase):
|
|
386
401
|
def test_view_detail_anonymous(self):
|
|
387
402
|
# Make the request as an unauthenticated user
|
|
@@ -413,8 +428,8 @@ class MarketplaceViewTest(TestCase):
|
|
|
413
428
|
self.assertHttpStatus(response, 200)
|
|
414
429
|
|
|
415
430
|
|
|
431
|
+
@tag("example_app")
|
|
416
432
|
class AppAPITest(APIViewTestCases.APIViewTestCase):
|
|
417
|
-
model = ExampleModel
|
|
418
433
|
bulk_update_data = {
|
|
419
434
|
"number": 2600,
|
|
420
435
|
}
|
|
@@ -436,6 +451,9 @@ class AppAPITest(APIViewTestCases.APIViewTestCase):
|
|
|
436
451
|
|
|
437
452
|
@classmethod
|
|
438
453
|
def setUpTestData(cls):
|
|
454
|
+
from example_app.models import ExampleModel
|
|
455
|
+
|
|
456
|
+
cls.model = ExampleModel
|
|
439
457
|
# Example objects to test.
|
|
440
458
|
ExampleModel.objects.create(name="Example 1", number=1)
|
|
441
459
|
ExampleModel.objects.create(name="Example 2", number=2)
|
|
@@ -443,6 +461,8 @@ class AppAPITest(APIViewTestCases.APIViewTestCase):
|
|
|
443
461
|
|
|
444
462
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
445
463
|
def test_api_urls(self):
|
|
464
|
+
from example_app.models import ExampleModel
|
|
465
|
+
|
|
446
466
|
# Test list URL resolution
|
|
447
467
|
list_url = reverse("plugins-api:example_app-api:examplemodel-list")
|
|
448
468
|
self.assertEqual(list_url, self._get_list_url())
|
|
@@ -509,6 +529,7 @@ class TestUserContextCustomValidator(CustomValidator):
|
|
|
509
529
|
self.validation_error(f"TestUserContextCustomValidator: user is {self.context['user']}")
|
|
510
530
|
|
|
511
531
|
|
|
532
|
+
@tag("example_app")
|
|
512
533
|
class AppCustomValidationTest(TestCase):
|
|
513
534
|
def setUp(self):
|
|
514
535
|
# When creating a fresh test DB, wrapping model clean methods fails, which is normal.
|
|
@@ -571,13 +592,15 @@ class AppCustomValidationTest(TestCase):
|
|
|
571
592
|
registry["plugin_custom_validators"]["dcim.locationtype"] = before
|
|
572
593
|
|
|
573
594
|
|
|
595
|
+
@tag("example_app")
|
|
574
596
|
class ExampleModelCustomActionViewTest(TestCase):
|
|
575
597
|
"""Test for custom action view `all_names` added to Example App"""
|
|
576
598
|
|
|
577
|
-
model = ExampleModel
|
|
578
|
-
|
|
579
599
|
@classmethod
|
|
580
600
|
def setUpTestData(cls):
|
|
601
|
+
from example_app.models import ExampleModel
|
|
602
|
+
|
|
603
|
+
cls.model = ExampleModel
|
|
581
604
|
ExampleModel.objects.create(name="Example 1", number=100)
|
|
582
605
|
ExampleModel.objects.create(name="Example 2", number=200)
|
|
583
606
|
ExampleModel.objects.create(name="Example 3", number=300)
|
|
@@ -633,6 +656,7 @@ class ExampleModelCustomActionViewTest(TestCase):
|
|
|
633
656
|
self.assertNotIn(example_model.name, response_body, msg=response_body)
|
|
634
657
|
|
|
635
658
|
|
|
659
|
+
@tag("example_app")
|
|
636
660
|
class FilterExtensionTest(TestCase):
|
|
637
661
|
"""
|
|
638
662
|
Tests for adding filter extensions
|
|
@@ -882,6 +906,7 @@ class LoadPluginTest(TestCase):
|
|
|
882
906
|
load_plugin(app_name, settings)
|
|
883
907
|
|
|
884
908
|
|
|
909
|
+
@tag("example_app", "example_app_with_view_override")
|
|
885
910
|
class TestAppCoreViewOverrides(TestCase):
|
|
886
911
|
"""
|
|
887
912
|
Validate that overridden core views work as expected.
|
|
@@ -916,6 +941,7 @@ class TestAppCoreViewOverrides(TestCase):
|
|
|
916
941
|
)
|
|
917
942
|
|
|
918
943
|
|
|
944
|
+
@tag("example_app")
|
|
919
945
|
class PluginTemplateExtensionsTest(TestCase):
|
|
920
946
|
"""
|
|
921
947
|
Test that registered TemplateExtensions inject content as expected
|