nautobot 3.0.0a2__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nautobot/apps/choices.py +4 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +13 -1
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/cloud/filters.py +1 -1
- nautobot/cloud/navigation.py +3 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +6 -1
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +12 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
- nautobot/core/cli/migrate_deprecated_templates.py +227 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +62 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +35 -19
- nautobot/core/settings.yaml +17 -33
- nautobot/core/signals.py +12 -1
- nautobot/core/tables.py +13 -6
- nautobot/core/templates/40x.html +1 -1
- nautobot/core/templates/500.html +2 -2
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/admin/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/buttons/export.html +1 -1
- nautobot/core/templates/components/button/dropdown.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/panel/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +6 -7
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +22 -9
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +6 -5
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- nautobot/core/templates/generic/object_bulk_edit.html +1 -1
- nautobot/core/templates/generic/object_bulk_import.html +1 -1
- nautobot/core/templates/generic/object_bulk_remove.html +2 -2
- nautobot/core/templates/generic/object_bulk_update.html +5 -4
- nautobot/core/templates/generic/object_create.html +5 -4
- nautobot/core/templates/generic/object_delete.html +1 -1
- nautobot/core/templates/generic/object_detail.html +1 -1
- nautobot/core/templates/generic/object_edit.html +1 -1
- nautobot/core/templates/generic/object_import.html +2 -1
- nautobot/core/templates/generic/object_list.html +12 -4
- nautobot/core/templates/generic/object_notes.html +5 -3
- nautobot/core/templates/generic/object_retrieve.html +4 -5
- nautobot/core/templates/graphene/graphiql.html +7 -8
- nautobot/core/templates/home.html +1 -1
- nautobot/core/templates/import_success.html +2 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/created_updated.html +7 -3
- nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +11 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +3 -9
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -11
- nautobot/core/templates/redoc_ui.html +7 -0
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +17 -3
- nautobot/core/templates/system_jobs/import_objects.html +1 -2
- nautobot/core/templates/utilities/confirmation_form.html +2 -2
- nautobot/core/templates/utilities/obj_table.html +10 -2
- nautobot/core/templates/utilities/render_field.html +7 -7
- nautobot/core/templates/utilities/render_jinja2.html +2 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
- nautobot/core/templates/utilities/theme_preview.html +19 -3
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +76 -18
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +41 -17
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -13
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/integration/test_filters.py +48 -11
- nautobot/core/tests/integration/test_theme.py +22 -21
- nautobot/core/tests/nautobot_config.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +8 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +27 -28
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_cli.py +40 -0
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_forms.py +55 -13
- nautobot/core/tests/test_jobs.py +144 -3
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +76 -1
- nautobot/core/tests/test_patch_social_django.py +42 -0
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +62 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +123 -4
- nautobot/core/tests/test_utils.py +72 -5
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +70 -29
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +143 -27
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +143 -55
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +20 -9
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +28 -18
- nautobot/core/utils/lookup.py +49 -8
- nautobot/core/utils/module_loading.py +21 -0
- nautobot/core/utils/patch_social_django.py +128 -0
- nautobot/core/views/__init__.py +38 -1
- nautobot/core/views/generic.py +3 -3
- nautobot/core/views/mixins.py +45 -22
- nautobot/core/views/renderers.py +4 -3
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/apps.py +1 -5
- nautobot/data_validation/custom_validators.py +4 -4
- nautobot/data_validation/filters.py +1 -1
- nautobot/data_validation/forms.py +40 -0
- nautobot/data_validation/migrations/0001_initial.py +0 -7
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
- nautobot/data_validation/models.py +16 -7
- nautobot/data_validation/navigation.py +8 -1
- nautobot/data_validation/tables.py +12 -5
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +2 -2
- nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
- nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
- nautobot/data_validation/tests/test_filters.py +8 -6
- nautobot/data_validation/tests/test_models.py +15 -0
- nautobot/data_validation/tests/test_views.py +190 -32
- nautobot/data_validation/urls.py +2 -5
- nautobot/data_validation/views.py +73 -40
- nautobot/dcim/api/serializers.py +3 -13
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +65 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
- nautobot/dcim/forms.py +90 -4
- nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
- nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
- nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
- nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
- nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
- nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
- nautobot/dcim/models/device_component_templates.py +33 -1
- nautobot/dcim/models/device_components.py +98 -64
- nautobot/dcim/models/devices.py +30 -20
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +18 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +15 -15
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_trace.html +0 -4
- nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
- nautobot/dcim/templates/dcim/consoleport.html +7 -6
- nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +2 -2
- nautobot/dcim/templates/dcim/device_create.html +5 -3
- nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
- nautobot/dcim/templates/dcim/device_list.html +73 -10
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
- nautobot/dcim/templates/dcim/frontport.html +10 -9
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
- nautobot/dcim/templates/dcim/interface.html +35 -7
- nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/module_frontports.html +1 -1
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/module_powerports.html +1 -1
- nautobot/dcim/templates/dcim/module_rearports.html +1 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +6 -5
- nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
- nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
- nautobot/dcim/templates/dcim/rearport.html +8 -7
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
- nautobot/dcim/tests/integration/test_controller.py +4 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
- nautobot/dcim/tests/integration/test_create_device.py +0 -2
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
- nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
- nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
- nautobot/dcim/tests/test_api.py +194 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +55 -7
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +328 -4
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +132 -29
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +777 -397
- nautobot/extras/api/views.py +60 -45
- nautobot/extras/choices.py +2 -13
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
- nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
- nautobot/extras/{filters/__init__.py → filters.py} +33 -48
- nautobot/extras/forms/forms.py +14 -15
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +11 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +33 -14
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +60 -4
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +23 -2
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/navigation.py +13 -9
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -9
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +64 -68
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/approval_dashboard.html +7 -5
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +0 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
- nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
- nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
- nautobot/extras/templates/extras/inc/json_format.html +10 -3
- nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
- nautobot/extras/templates/extras/job.html +29 -12
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
- nautobot/extras/templates/extras/job_edit.html +52 -46
- nautobot/extras/templates/extras/job_list.html +29 -25
- nautobot/extras/templates/extras/marketplace.html +5 -9
- nautobot/extras/templates/extras/object_configcontext.html +1 -1
- nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
- nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
- nautobot/extras/templates/extras/plugin_detail.html +29 -24
- nautobot/extras/templates/extras/plugins_list.html +16 -26
- nautobot/extras/templates/extras/role_retrieve.html +64 -0
- nautobot/extras/templates/extras/scheduledjob.html +4 -2
- nautobot/extras/templates/extras/secret_create.html +1 -1
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_computedfields.py +8 -9
- nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +9 -10
- nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +90 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +59 -5
- nautobot/extras/tests/test_customfields.py +22 -13
- nautobot/extras/tests/test_customfields_filters.py +479 -0
- nautobot/extras/tests/test_dynamicgroups.py +39 -1
- nautobot/extras/tests/test_filters.py +57 -22
- nautobot/extras/tests/test_forms.py +18 -21
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +1 -0
- nautobot/extras/tests/test_models.py +51 -33
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +52 -112
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +164 -71
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +53 -38
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -2
- nautobot/ipam/signals.py +71 -0
- nautobot/ipam/tables.py +19 -6
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
- nautobot/ipam/templates/ipam/ipaddress.html +14 -0
- nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +13 -6
- nautobot/ipam/tests/test_filters.py +36 -1
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +44 -2
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +13 -14
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +53 -11
- nautobot/load_balancers/__init__.py +0 -0
- nautobot/load_balancers/api/__init__.py +1 -0
- nautobot/load_balancers/api/serializers.py +75 -0
- nautobot/load_balancers/api/urls.py +23 -0
- nautobot/load_balancers/api/views.py +61 -0
- nautobot/load_balancers/apps.py +17 -0
- nautobot/load_balancers/choices.py +167 -0
- nautobot/load_balancers/filters.py +225 -0
- nautobot/load_balancers/forms.py +532 -0
- nautobot/load_balancers/management/commands/__init__.py +0 -0
- nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
- nautobot/load_balancers/migrations/0001_initial.py +465 -0
- nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
- nautobot/load_balancers/migrations/__init__.py +0 -0
- nautobot/load_balancers/models.py +423 -0
- nautobot/load_balancers/navigation.py +80 -0
- nautobot/load_balancers/tables.py +255 -0
- nautobot/load_balancers/tests/__init__.py +474 -0
- nautobot/load_balancers/tests/test_api.py +353 -0
- nautobot/load_balancers/tests/test_filters.py +134 -0
- nautobot/load_balancers/tests/test_forms.py +266 -0
- nautobot/load_balancers/tests/test_models.py +195 -0
- nautobot/load_balancers/tests/test_views.py +229 -0
- nautobot/load_balancers/urls.py +17 -0
- nautobot/load_balancers/views.py +248 -0
- nautobot/project-static/dist/css/github-dark.min.css +10 -0
- nautobot/project-static/dist/css/github.min.css +10 -0
- nautobot/project-static/dist/css/nautobot.css +1 -11
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/forms.js +13 -0
- nautobot/project-static/js/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +1 -1
- nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
- nautobot/project-static/nautobot-icons/elements.svg +1 -1
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +1 -1
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +1 -1
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +1 -1
- nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
- nautobot/project-static/nautobot-icons/star.svg +1 -1
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +112 -30
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/users/templates/users/profile.html +45 -12
- nautobot/users/templates/users/sessionkey_delete.html +1 -1
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/views.py +4 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/models.py +1 -68
- nautobot/virtualization/navigation.py +3 -2
- nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +45 -4
- nautobot/virtualization/views.py +4 -1
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +219 -0
- nautobot/vpn/filters.py +234 -0
- nautobot/vpn/forms.py +487 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +535 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +383 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +336 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +293 -0
- nautobot/vpn/tests/test_models.py +147 -0
- nautobot/vpn/tests/test_views.py +300 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +495 -0
- nautobot/wireless/navigation.py +3 -2
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
- nautobot/circuits/templates/circuits/circuit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuit_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittermination.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittype.html +0 -2
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
- nautobot/circuits/templates/circuits/provider.html +0 -2
- nautobot/circuits/templates/circuits/provider_edit.html +0 -2
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
- nautobot/circuits/templates/circuits/provider_update.html +0 -1
- nautobot/circuits/templates/circuits/providernetwork.html +0 -2
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
- nautobot/core/templates/buttons/import.html +0 -9
- nautobot/data_validation/template_content.py +0 -42
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/dcim/templates/dcim/controller/base.html +0 -2
- nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/device/base.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
- nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
- nautobot/dcim/templates/dcim/device/frontports.html +0 -2
- nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
- nautobot/dcim/templates/dcim/device/inventory.html +0 -2
- nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
- nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
- nautobot/dcim/templates/dcim/device/powerports.html +0 -2
- nautobot/dcim/templates/dcim/device/rearports.html +0 -2
- nautobot/dcim/templates/dcim/device/wireless.html +0 -2
- nautobot/dcim/templates/dcim/device_component.html +0 -2
- nautobot/dcim/templates/dcim/device_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/devicetype.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/location.html +0 -2
- nautobot/dcim/templates/dcim/location_edit.html +0 -2
- nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
- nautobot/dcim/templates/dcim/locationtype.html +0 -2
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/manufacturer.html +0 -2
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/platform.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/rack.html +0 -2
- nautobot/dcim/templates/dcim/rack_edit.html +0 -2
- nautobot/dcim/templates/dcim/rackgroup.html +0 -2
- nautobot/dcim/templates/dcim/rackreservation.html +0 -2
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
- nautobot/dcim/ui.py +0 -29
- nautobot/extras/templates/extras/computedfield.html +0 -2
- nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontext.html +0 -2
- nautobot/extras/templates/extras/configcontext_edit.html +0 -2
- nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontextschema.html +0 -2
- nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
- nautobot/extras/templates/extras/contact_retrieve.html +0 -2
- nautobot/extras/templates/extras/customfield.html +0 -2
- nautobot/extras/templates/extras/customfield_edit.html +0 -2
- nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/customlink.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
- nautobot/extras/templates/extras/exporttemplate.html +0 -2
- nautobot/extras/templates/extras/gitrepository.html +0 -2
- nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
- nautobot/extras/templates/extras/graphqlquery.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
- nautobot/extras/templates/extras/job_detail.html +0 -2
- nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobhook.html +0 -2
- nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobresult.html +0 -2
- nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
- nautobot/extras/templates/extras/note.html +0 -2
- nautobot/extras/templates/extras/note_retrieve.html +0 -1
- nautobot/extras/templates/extras/object_changelog.html +0 -2
- nautobot/extras/templates/extras/object_notes.html +0 -2
- nautobot/extras/templates/extras/objectchange.html +0 -2
- nautobot/extras/templates/extras/objectchange_list.html +0 -3
- nautobot/extras/templates/extras/relationship.html +0 -1
- nautobot/extras/templates/extras/secret.html +0 -1
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
- nautobot/extras/templates/extras/status.html +0 -2
- nautobot/extras/templates/extras/tag.html +0 -2
- nautobot/extras/templates/extras/tag_edit.html +0 -2
- nautobot/extras/templates/extras/tag_retrieve.html +0 -2
- nautobot/extras/templates/extras/team_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/prefix.html +0 -2
- nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/rir.html +0 -2
- nautobot/ipam/templates/ipam/routetarget.html +0 -1
- nautobot/ipam/templates/ipam/service.html +0 -2
- nautobot/ipam/templates/ipam/service_edit.html +0 -2
- nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlan.html +0 -2
- nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
- nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlangroup.html +0 -2
- nautobot/ipam/templates/ipam/vrf.html +0 -1
- nautobot/tenancy/templates/tenancy/tenant.html +0 -2
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
- /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
|
@@ -15,6 +15,7 @@ from nautobot.core.forms import (
|
|
|
15
15
|
TagFilterField,
|
|
16
16
|
)
|
|
17
17
|
from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
|
|
18
|
+
from nautobot.core.utils.config import get_settings_or_config
|
|
18
19
|
from nautobot.data_validation.models import (
|
|
19
20
|
DataCompliance,
|
|
20
21
|
MinMaxValidationRule,
|
|
@@ -22,6 +23,8 @@ from nautobot.data_validation.models import (
|
|
|
22
23
|
RequiredValidationRule,
|
|
23
24
|
UniqueValidationRule,
|
|
24
25
|
)
|
|
26
|
+
from nautobot.dcim.choices import DeviceUniquenessChoices
|
|
27
|
+
from nautobot.dcim.models import Device
|
|
25
28
|
from nautobot.extras.forms import (
|
|
26
29
|
NautobotBulkEditForm,
|
|
27
30
|
NautobotFilterForm,
|
|
@@ -300,3 +303,40 @@ class DataComplianceFilterForm(BootstrapMixin, forms.Form):
|
|
|
300
303
|
required=False,
|
|
301
304
|
)
|
|
302
305
|
q = forms.CharField(required=False, label="Search")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
#
|
|
309
|
+
# Device Constraints
|
|
310
|
+
#
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class DeviceConstraintsForm(BootstrapMixin, forms.Form):
|
|
314
|
+
DEVICE_UNIQUENESS = forms.ChoiceField(
|
|
315
|
+
choices=DeviceUniquenessChoices.CHOICES,
|
|
316
|
+
label="Device Uniqueness",
|
|
317
|
+
required=True,
|
|
318
|
+
error_messages={
|
|
319
|
+
"invalid_choice": f"Invalid value. Available options are: {', '.join(DeviceUniquenessChoices.values())}"
|
|
320
|
+
},
|
|
321
|
+
)
|
|
322
|
+
DEVICE_NAME_REQUIRED = forms.BooleanField(
|
|
323
|
+
label="Device name required (cannot be blank or null)",
|
|
324
|
+
initial=False,
|
|
325
|
+
required=False,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def __init__(self, *args, user=None, **kwargs):
|
|
329
|
+
super().__init__(*args, **kwargs)
|
|
330
|
+
|
|
331
|
+
self.fields["DEVICE_UNIQUENESS"].initial = get_settings_or_config(
|
|
332
|
+
"DEVICE_UNIQUENESS", fallback=DeviceUniquenessChoices.DEFAULT
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
device_ct = ContentType.objects.get_for_model(Device)
|
|
336
|
+
name_rule_exists = RequiredValidationRule.objects.filter(content_type=device_ct, field="name").exists()
|
|
337
|
+
|
|
338
|
+
self.fields["DEVICE_NAME_REQUIRED"].initial = name_rule_exists
|
|
339
|
+
|
|
340
|
+
if user is not None and not user.is_staff:
|
|
341
|
+
for field in self.fields.values():
|
|
342
|
+
field.disabled = True
|
|
@@ -198,12 +198,6 @@ class Migration(migrations.Migration):
|
|
|
198
198
|
default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True
|
|
199
199
|
),
|
|
200
200
|
),
|
|
201
|
-
("created", models.DateTimeField(auto_now_add=True, null=True)),
|
|
202
|
-
("last_updated", models.DateTimeField(auto_now=True, null=True)),
|
|
203
|
-
(
|
|
204
|
-
"_custom_field_data",
|
|
205
|
-
models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
|
|
206
|
-
),
|
|
207
201
|
("compliance_class_name", models.CharField(max_length=255)),
|
|
208
202
|
("last_validation_date", models.DateTimeField(auto_now=True)),
|
|
209
203
|
("object_id", models.UUIDField(blank=False, null=False)),
|
|
@@ -216,7 +210,6 @@ class Migration(migrations.Migration):
|
|
|
216
210
|
"content_type",
|
|
217
211
|
models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="contenttypes.contenttype"),
|
|
218
212
|
),
|
|
219
|
-
("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
|
|
220
213
|
],
|
|
221
214
|
options={
|
|
222
215
|
"verbose_name_plural": "Data Compliance",
|
|
@@ -13,7 +13,8 @@ def update_data_validation_engine_job_module_name(apps, schema_editor):
|
|
|
13
13
|
"""
|
|
14
14
|
Job = apps.get_model("extras", "Job")
|
|
15
15
|
dve_jobs = Job.objects.filter(module_name="nautobot_data_validation_engine.jobs")
|
|
16
|
-
|
|
16
|
+
# Now that the DVE jobs are system jobs, they should be enabled by default
|
|
17
|
+
dve_jobs.update(module_name="nautobot.core.jobs", enabled=True)
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def update_data_validation_engine_git_repo_contents(apps, schema_editor):
|
|
@@ -136,9 +137,6 @@ def copy_app_data_to_core_data(apps, schema_editor):
|
|
|
136
137
|
f"""\
|
|
137
138
|
INSERT INTO data_validation_datacompliance (
|
|
138
139
|
id,
|
|
139
|
-
created,
|
|
140
|
-
last_updated,
|
|
141
|
-
_custom_field_data,
|
|
142
140
|
compliance_class_name,
|
|
143
141
|
last_validation_date,
|
|
144
142
|
content_type_id,
|
|
@@ -150,9 +148,6 @@ INSERT INTO data_validation_datacompliance (
|
|
|
150
148
|
message
|
|
151
149
|
) SELECT
|
|
152
150
|
id,
|
|
153
|
-
created,
|
|
154
|
-
last_updated,
|
|
155
|
-
_custom_field_data,
|
|
156
151
|
compliance_class_name,
|
|
157
152
|
last_validation_date,
|
|
158
153
|
content_type_id,
|
|
@@ -176,7 +171,7 @@ def revert_data_validation_engine_job_module_name(apps, schema_editor):
|
|
|
176
171
|
Revert the `module_name` for the Jobs to match the old location of the data validation engine.
|
|
177
172
|
"""
|
|
178
173
|
Job = apps.get_model("extras", "Job")
|
|
179
|
-
dve_jobs = Job.objects.filter(module_name="nautobot.
|
|
174
|
+
dve_jobs = Job.objects.filter(module_name="nautobot.core.jobs")
|
|
180
175
|
dve_jobs.update(module_name="nautobot_data_validation_engine.jobs")
|
|
181
176
|
|
|
182
177
|
|
|
@@ -274,9 +269,6 @@ def copy_core_data_to_app_data(apps, schema_editor):
|
|
|
274
269
|
f"""\
|
|
275
270
|
INSERT INTO nautobot_data_validation_engine_datacompliance (
|
|
276
271
|
id,
|
|
277
|
-
created,
|
|
278
|
-
last_updated,
|
|
279
|
-
_custom_field_data,
|
|
280
272
|
compliance_class_name,
|
|
281
273
|
last_validation_date,
|
|
282
274
|
content_type_id,
|
|
@@ -288,9 +280,6 @@ INSERT INTO nautobot_data_validation_engine_datacompliance (
|
|
|
288
280
|
message
|
|
289
281
|
) SELECT
|
|
290
282
|
id,
|
|
291
|
-
created,
|
|
292
|
-
last_updated,
|
|
293
|
-
_custom_field_data,
|
|
294
283
|
compliance_class_name,
|
|
295
284
|
last_validation_date,
|
|
296
285
|
content_type_id,
|
|
@@ -9,10 +9,11 @@ from django.core.validators import MinValueValidator, ValidationError
|
|
|
9
9
|
from django.db import models
|
|
10
10
|
|
|
11
11
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
12
|
-
from nautobot.core.models import BaseManager
|
|
12
|
+
from nautobot.core.models import BaseManager, BaseModel
|
|
13
13
|
from nautobot.core.models.generics import PrimaryModel
|
|
14
14
|
from nautobot.core.models.querysets import RestrictedQuerySet
|
|
15
15
|
from nautobot.core.utils.cache import construct_cache_key
|
|
16
|
+
from nautobot.extras.models.mixins import DynamicGroupsModelMixin, NotesMixin, SavedViewMixin
|
|
16
17
|
from nautobot.extras.utils import extras_features, FeatureQuery
|
|
17
18
|
|
|
18
19
|
|
|
@@ -62,7 +63,7 @@ class ValidationRuleManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
62
63
|
return construct_cache_key(self, method_name="get_enabled_for_model", branch_aware=True)
|
|
63
64
|
|
|
64
65
|
|
|
65
|
-
class
|
|
66
|
+
class ValidationRuleModelMixin(models.Model):
|
|
66
67
|
"""Base model for all validation engine rule models."""
|
|
67
68
|
|
|
68
69
|
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
|
|
@@ -83,6 +84,8 @@ class ValidationRule(PrimaryModel):
|
|
|
83
84
|
objects = ValidationRuleManager()
|
|
84
85
|
documentation_static_path = "docs/user-guide/platform-functionality/data-validation.html"
|
|
85
86
|
|
|
87
|
+
is_data_compliance_model = False
|
|
88
|
+
|
|
86
89
|
class Meta:
|
|
87
90
|
"""Model metadata for all validation engine rule models."""
|
|
88
91
|
|
|
@@ -101,7 +104,7 @@ class ValidationRule(PrimaryModel):
|
|
|
101
104
|
"relationships",
|
|
102
105
|
"webhooks",
|
|
103
106
|
)
|
|
104
|
-
class RegularExpressionValidationRule(
|
|
107
|
+
class RegularExpressionValidationRule(ValidationRuleModelMixin, PrimaryModel):
|
|
105
108
|
"""A type of validation rule that applies a regular expression to a given model field."""
|
|
106
109
|
|
|
107
110
|
regular_expression = models.TextField()
|
|
@@ -165,7 +168,7 @@ class RegularExpressionValidationRule(ValidationRule):
|
|
|
165
168
|
"relationships",
|
|
166
169
|
"webhooks",
|
|
167
170
|
)
|
|
168
|
-
class MinMaxValidationRule(
|
|
171
|
+
class MinMaxValidationRule(ValidationRuleModelMixin, PrimaryModel):
|
|
169
172
|
"""A type of validation rule that applies min/max constraints to a given numeric model field."""
|
|
170
173
|
|
|
171
174
|
min = models.FloatField(
|
|
@@ -231,7 +234,7 @@ class MinMaxValidationRule(ValidationRule):
|
|
|
231
234
|
"relationships",
|
|
232
235
|
"webhooks",
|
|
233
236
|
)
|
|
234
|
-
class RequiredValidationRule(
|
|
237
|
+
class RequiredValidationRule(ValidationRuleModelMixin, PrimaryModel):
|
|
235
238
|
"""A type of validation rule that applies a required constraint to a given model field."""
|
|
236
239
|
|
|
237
240
|
clone_fields = ["enabled", "content_type", "error_message"]
|
|
@@ -279,7 +282,7 @@ class RequiredValidationRule(ValidationRule):
|
|
|
279
282
|
"relationships",
|
|
280
283
|
"webhooks",
|
|
281
284
|
)
|
|
282
|
-
class UniqueValidationRule(
|
|
285
|
+
class UniqueValidationRule(ValidationRuleModelMixin, PrimaryModel):
|
|
283
286
|
"""
|
|
284
287
|
A type of validation rule that applies a unique constraint to a given model field.
|
|
285
288
|
|
|
@@ -321,7 +324,11 @@ class UniqueValidationRule(ValidationRule):
|
|
|
321
324
|
raise ValidationError({"field": "This field is already unique by default."})
|
|
322
325
|
|
|
323
326
|
|
|
324
|
-
|
|
327
|
+
@extras_features(
|
|
328
|
+
"export_templates",
|
|
329
|
+
"graphql",
|
|
330
|
+
)
|
|
331
|
+
class DataCompliance(DynamicGroupsModelMixin, NotesMixin, SavedViewMixin, BaseModel):
|
|
325
332
|
"""Model to represent the results of an audit method."""
|
|
326
333
|
|
|
327
334
|
compliance_class_name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=False, null=False)
|
|
@@ -335,6 +342,8 @@ class DataCompliance(PrimaryModel):
|
|
|
335
342
|
valid = models.BooleanField(blank=False, null=False)
|
|
336
343
|
message = models.TextField(blank=True, default="")
|
|
337
344
|
|
|
345
|
+
is_data_compliance_model = False
|
|
346
|
+
|
|
338
347
|
class Meta:
|
|
339
348
|
"""Meta class for Audit model."""
|
|
340
349
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""App navigation menu items."""
|
|
2
2
|
|
|
3
3
|
from nautobot.apps.ui import NavMenuAddButton, NavMenuGroup, NavMenuItem, NavMenuTab
|
|
4
|
+
from nautobot.core.ui.choices import NavigationIconChoices, NavigationWeightChoices
|
|
4
5
|
|
|
5
6
|
menu_items = (
|
|
6
7
|
NavMenuTab(
|
|
7
8
|
name="Extensibility",
|
|
8
|
-
icon=
|
|
9
|
+
icon=NavigationIconChoices.EXTENSIBILITY,
|
|
10
|
+
weight=NavigationWeightChoices.EXTENSIBILITY,
|
|
9
11
|
groups=(
|
|
10
12
|
NavMenuGroup(
|
|
11
13
|
name="Data Validation",
|
|
@@ -60,6 +62,11 @@ menu_items = (
|
|
|
60
62
|
name="Data Compliance",
|
|
61
63
|
permissions=["data_validation.view_datacompliance"],
|
|
62
64
|
),
|
|
65
|
+
NavMenuItem(
|
|
66
|
+
link="data_validation:device-constraints",
|
|
67
|
+
name="Device Constraints",
|
|
68
|
+
permissions=["dcim.view_device"],
|
|
69
|
+
),
|
|
63
70
|
),
|
|
64
71
|
),
|
|
65
72
|
),
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from django.utils.html import format_html
|
|
4
4
|
import django_tables2 as tables
|
|
5
5
|
|
|
6
|
-
from nautobot.core.tables import BaseTable, TagColumn, ToggleColumn
|
|
6
|
+
from nautobot.core.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn
|
|
7
7
|
from nautobot.data_validation.models import (
|
|
8
8
|
DataCompliance,
|
|
9
9
|
MinMaxValidationRule,
|
|
@@ -21,7 +21,9 @@ class RegularExpressionValidationRuleTable(BaseTable):
|
|
|
21
21
|
"""Base table for the RegularExpressionValidationRule model."""
|
|
22
22
|
|
|
23
23
|
pk = ToggleColumn()
|
|
24
|
-
name = tables.
|
|
24
|
+
name = tables.Column(linkify=True, order_by=("name",))
|
|
25
|
+
enabled = BooleanColumn()
|
|
26
|
+
context_processing = BooleanColumn()
|
|
25
27
|
tags = TagColumn()
|
|
26
28
|
|
|
27
29
|
class Meta(BaseTable.Meta):
|
|
@@ -60,7 +62,8 @@ class MinMaxValidationRuleTable(BaseTable):
|
|
|
60
62
|
"""Base table for the MinMaxValidationRule model."""
|
|
61
63
|
|
|
62
64
|
pk = ToggleColumn()
|
|
63
|
-
name = tables.
|
|
65
|
+
name = tables.Column(linkify=True, order_by=("name",))
|
|
66
|
+
enabled = BooleanColumn()
|
|
64
67
|
tags = TagColumn()
|
|
65
68
|
|
|
66
69
|
class Meta(BaseTable.Meta):
|
|
@@ -99,7 +102,8 @@ class RequiredValidationRuleTable(BaseTable):
|
|
|
99
102
|
"""Base table for the RequiredValidationRule model."""
|
|
100
103
|
|
|
101
104
|
pk = ToggleColumn()
|
|
102
|
-
name = tables.
|
|
105
|
+
name = tables.Column(linkify=True, order_by=("name",))
|
|
106
|
+
enabled = BooleanColumn()
|
|
103
107
|
tags = TagColumn()
|
|
104
108
|
|
|
105
109
|
class Meta(BaseTable.Meta):
|
|
@@ -134,7 +138,8 @@ class UniqueValidationRuleTable(BaseTable):
|
|
|
134
138
|
"""Base table for the UniqueValidationRule model."""
|
|
135
139
|
|
|
136
140
|
pk = ToggleColumn()
|
|
137
|
-
name = tables.
|
|
141
|
+
name = tables.Column(linkify=True, order_by=("name",))
|
|
142
|
+
enabled = BooleanColumn()
|
|
138
143
|
tags = TagColumn()
|
|
139
144
|
|
|
140
145
|
class Meta(BaseTable.Meta):
|
|
@@ -186,6 +191,7 @@ class DataComplianceTable(BaseTable):
|
|
|
186
191
|
id = tables.Column(linkify=True, verbose_name="ID")
|
|
187
192
|
validated_object = tables.RelatedLinkColumn()
|
|
188
193
|
validated_attribute = ValidatedAttributeColumn()
|
|
194
|
+
valid = BooleanColumn()
|
|
189
195
|
|
|
190
196
|
def order_validated_object(self, queryset, is_descending):
|
|
191
197
|
"""Reorder table by string representation of validated_object."""
|
|
@@ -226,6 +232,7 @@ class DataComplianceTableTab(BaseTable):
|
|
|
226
232
|
"""Base table for viewing the DataCompliance related to a single object."""
|
|
227
233
|
|
|
228
234
|
validated_attribute = ValidatedAttributeColumn()
|
|
235
|
+
valid = BooleanColumn()
|
|
229
236
|
|
|
230
237
|
class Meta(BaseTable.Meta):
|
|
231
238
|
"""Meta class for DataComplianceTableTab."""
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% load form_helpers %}
|
|
3
|
+
{% load ui_framework %}
|
|
4
|
+
|
|
5
|
+
{% block breadcrumbs_wrapper %}{% render_breadcrumbs %}{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block content %}
|
|
8
|
+
<form action="." method="post" class="h-100 vstack">
|
|
9
|
+
{% csrf_token %}
|
|
10
|
+
<div class="alert alert-warning d-flex align-items-center" role="alert">
|
|
11
|
+
<span aria-hidden="true" class="mdi mdi-alert me-2"></span>
|
|
12
|
+
<div>
|
|
13
|
+
<strong>Warning:</strong> Changes made here affect how device uniqueness and naming rules are enforced.
|
|
14
|
+
These changes will update the Constance configuration for <code>DEVICE_UNIQUENESS</code> and will create
|
|
15
|
+
a <code>RequiredValidationRule</code> for the <code>Device</code> model if
|
|
16
|
+
<i>Device name required</i> is selected.
|
|
17
|
+
Modify these settings <strong>at your own risk</strong>.
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="row align-content-start flex-fill">
|
|
21
|
+
<div class="col-md-8 offset-md-2">
|
|
22
|
+
|
|
23
|
+
{% if form.non_field_errors %}
|
|
24
|
+
<div class="card border-danger mb-16">
|
|
25
|
+
<div class="card-header bg-danger-subtle border-danger text-body">
|
|
26
|
+
<strong>Errors</strong>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="card-body">
|
|
29
|
+
{{ form.non_field_errors }}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
{% endif %}
|
|
33
|
+
|
|
34
|
+
<div class="card mb-16">
|
|
35
|
+
<div class="card-header"><strong>Device Constraints</strong></div>
|
|
36
|
+
<div class="card-body">
|
|
37
|
+
{% render_field form.DEVICE_UNIQUENESS %}
|
|
38
|
+
{% render_field form.DEVICE_NAME_REQUIRED %}
|
|
39
|
+
{% if not request.user.is_staff %}
|
|
40
|
+
<p class="text-secondary mt-2 text-center">
|
|
41
|
+
<em>You do not have permission to modify these settings.</em>
|
|
42
|
+
</p>
|
|
43
|
+
{% endif %}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="nb-form-sticky-footer">
|
|
49
|
+
{% if request.user.is_staff %}
|
|
50
|
+
<button type="submit" name="_update" class="btn btn-primary">
|
|
51
|
+
<span aria-hidden="true" class="mdi mdi-check me-4"></span><!--
|
|
52
|
+
-->Update
|
|
53
|
+
</button>
|
|
54
|
+
<a href="{% url 'home' %}" class="btn btn-secondary">
|
|
55
|
+
<span aria-hidden="true" class="mdi mdi-close me-4"></span><!--
|
|
56
|
+
-->Cancel
|
|
57
|
+
</a>
|
|
58
|
+
{% endif %}
|
|
59
|
+
</div>
|
|
60
|
+
</form>
|
|
61
|
+
{% endblock %}
|
|
@@ -5,11 +5,11 @@ import contextlib
|
|
|
5
5
|
from django.core.cache import cache
|
|
6
6
|
import redis.exceptions
|
|
7
7
|
|
|
8
|
-
from nautobot.data_validation.models import
|
|
8
|
+
from nautobot.data_validation.models import ValidationRuleModelMixin
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ValidationRuleTestCaseMixin:
|
|
12
|
-
model: type(
|
|
12
|
+
model: type(ValidationRuleModelMixin)
|
|
13
13
|
|
|
14
14
|
def tearDown(self):
|
|
15
15
|
"""Ensure that validation rule caches are cleared to avoid leakage into other tests."""
|
|
@@ -210,7 +210,26 @@ CREATE TABLE `nautobot_data_validation_engine_uniquevalidationrule` (
|
|
|
210
210
|
|
|
211
211
|
def _populate_tables_postgresql(ContentType, DeviceType, VLAN):
|
|
212
212
|
with connection.cursor() as cursor:
|
|
213
|
-
|
|
213
|
+
cursor.execute(
|
|
214
|
+
"""\
|
|
215
|
+
INSERT INTO nautobot_data_validation_engine_datacompliance
|
|
216
|
+
VALUES (
|
|
217
|
+
'f20e4572-84cf-4f28-9fe6-5f0f96f78d14',
|
|
218
|
+
'2025-10-21 16:07:55.123456+00',
|
|
219
|
+
'2025-10-21 16:07:55.123456+00',
|
|
220
|
+
'{}',
|
|
221
|
+
'DcimDevicetypeCustomValidator',
|
|
222
|
+
'2025-10-21 16:07:55.123456+00',
|
|
223
|
+
'96591cd4-c4d1-4d69-982d-195bcea71a2c',
|
|
224
|
+
'Juniper SRX300',
|
|
225
|
+
'part_number',
|
|
226
|
+
'',
|
|
227
|
+
'f',
|
|
228
|
+
'A device type may only contain alpha numeric, dashes, and underscore characters.',
|
|
229
|
+
%s
|
|
230
|
+
);""",
|
|
231
|
+
[ContentType.objects.get_for_model(DeviceType).id],
|
|
232
|
+
)
|
|
214
233
|
|
|
215
234
|
# Min/max validation rules
|
|
216
235
|
cursor.execute(
|
|
@@ -269,7 +288,26 @@ VALUES (
|
|
|
269
288
|
|
|
270
289
|
def _populate_tables_mysql(ContentType, DeviceType, VLAN):
|
|
271
290
|
with connection.cursor() as cursor:
|
|
272
|
-
|
|
291
|
+
cursor.execute(
|
|
292
|
+
"""\
|
|
293
|
+
INSERT INTO `nautobot_data_validation_engine_datacompliance`
|
|
294
|
+
VALUES (
|
|
295
|
+
'f20e457284cf4f289fe65f0f96f78d14',
|
|
296
|
+
'2025-10-21 16:07:55.123456',
|
|
297
|
+
'2025-10-21 16:07:55.123456',
|
|
298
|
+
'{}',
|
|
299
|
+
'DcimDevicetypeCustomValidator',
|
|
300
|
+
'2025-10-21 16:07:55.123456',
|
|
301
|
+
'96591cd4c4d14d69982d195bcea71a2c',
|
|
302
|
+
'Juniper SRX300',
|
|
303
|
+
'part_number',
|
|
304
|
+
'',
|
|
305
|
+
0,
|
|
306
|
+
'A device type may only contain alpha numeric, dashes, and underscore characters.',
|
|
307
|
+
%s
|
|
308
|
+
);""",
|
|
309
|
+
[ContentType.objects.get_for_model(DeviceType).id],
|
|
310
|
+
)
|
|
273
311
|
|
|
274
312
|
# Min/max validation rules
|
|
275
313
|
cursor.execute(
|
|
@@ -366,7 +404,7 @@ class DVEToDataValidationMigrationTestCase(MigratorTestCase):
|
|
|
366
404
|
UniqueValidationRule = self.new_state.apps.get_model("data_validation", "uniquevalidationrule")
|
|
367
405
|
|
|
368
406
|
with self.subTest("DataCompliance"):
|
|
369
|
-
self.assertEqual(DataCompliance.objects.count(),
|
|
407
|
+
self.assertEqual(DataCompliance.objects.count(), 1)
|
|
370
408
|
|
|
371
409
|
with self.subTest("MinMaxValidationRule"):
|
|
372
410
|
self.assertEqual(MinMaxValidationRule.objects.count(), 1)
|
|
@@ -397,6 +435,48 @@ class DataValidationToDVEMigrationTestCase(MigratorTestCase):
|
|
|
397
435
|
else:
|
|
398
436
|
raise ValueError(f"Unknown/unsupported database vendor {connection.vendor}")
|
|
399
437
|
|
|
438
|
+
ContentType = self.old_state.apps.get_model("contenttypes", "contenttype")
|
|
439
|
+
DeviceType = self.old_state.apps.get_model("dcim", "devicetype")
|
|
440
|
+
VLAN = self.old_state.apps.get_model("ipam", "vlan")
|
|
441
|
+
|
|
442
|
+
DataCompliance = self.old_state.apps.get_model("data_validation", "datacompliance")
|
|
443
|
+
MinMaxValidationRule = self.old_state.apps.get_model("data_validation", "minmaxvalidationrule")
|
|
444
|
+
RegularExpressionValidationRule = self.old_state.apps.get_model(
|
|
445
|
+
"data_validation", "regularexpressionvalidationrule"
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
DataCompliance.objects.create(
|
|
449
|
+
compliance_class_name="DcimDevicetypeCustomValidator",
|
|
450
|
+
object_id="96591cd4-c4d1-4d69-982d-195bcea71a2c",
|
|
451
|
+
validated_object_str="Juniper SRX300",
|
|
452
|
+
validated_attribute="part_number",
|
|
453
|
+
validated_attribute_value="",
|
|
454
|
+
valid=False,
|
|
455
|
+
message="A device type may only contain alpha numeric, dashes, and underscore characters.",
|
|
456
|
+
content_type=ContentType.objects.get_for_model(DeviceType),
|
|
457
|
+
)
|
|
458
|
+
MinMaxValidationRule.objects.create(
|
|
459
|
+
name="Max VLAN ID",
|
|
460
|
+
enabled=False,
|
|
461
|
+
error_message="",
|
|
462
|
+
field="vid",
|
|
463
|
+
min=None,
|
|
464
|
+
max=3999,
|
|
465
|
+
content_type=ContentType.objects.get_for_model(VLAN),
|
|
466
|
+
)
|
|
467
|
+
RegularExpressionValidationRule.objects.create(
|
|
468
|
+
name="Device Type Part Number",
|
|
469
|
+
enabled=True,
|
|
470
|
+
error_message="A device type may only contain alpha numeric, dashes, and underscore characters.",
|
|
471
|
+
field="part_number",
|
|
472
|
+
regular_expression="^[a-zA-Z0-9_-]+$",
|
|
473
|
+
content_type=ContentType.objects.get_for_model(DeviceType),
|
|
474
|
+
context_processing=False,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
# TODO: requiredvalidationrule
|
|
478
|
+
# TODO: uniquevalidationrule
|
|
479
|
+
|
|
400
480
|
def tearDown(self):
|
|
401
481
|
super().tearDown()
|
|
402
482
|
if connection.vendor == "postgresql":
|
|
@@ -19,10 +19,9 @@ class TestFailedDataComplianceRule(DataComplianceRule):
|
|
|
19
19
|
# attribute
|
|
20
20
|
raise ComplianceError(
|
|
21
21
|
{
|
|
22
|
-
"tenant": "
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"status": "Status",
|
|
22
|
+
"tenant": "The tenant is wrong",
|
|
23
|
+
"name": "The name is wrong",
|
|
24
|
+
"status": "The status is wrong",
|
|
26
25
|
}
|
|
27
26
|
)
|
|
28
27
|
|
|
@@ -36,6 +35,12 @@ class TestPassedDataComplianceRule(DataComplianceRule):
|
|
|
36
35
|
"""No exception means the audit passes."""
|
|
37
36
|
|
|
38
37
|
|
|
38
|
+
class TestFailedDataComplianceRuleAlt(TestFailedDataComplianceRule):
|
|
39
|
+
"""Test implementation of DataComplianceRule, for dcim.rack."""
|
|
40
|
+
|
|
41
|
+
model = "dcim.device"
|
|
42
|
+
|
|
43
|
+
|
|
39
44
|
class TestCompliance(TestCase):
|
|
40
45
|
"""Test DataComplianceRule methods."""
|
|
41
46
|
|
|
@@ -62,7 +67,7 @@ class TestCompliance(TestCase):
|
|
|
62
67
|
|
|
63
68
|
def test_audit_fail(self):
|
|
64
69
|
result = DataCompliance.objects.filter(valid=False).all()
|
|
65
|
-
self.assertEqual(len(result),
|
|
70
|
+
self.assertEqual(len(result), 4)
|
|
66
71
|
result = DataCompliance.objects.get(validated_attribute="tenant")
|
|
67
72
|
self.assertEqual(result.compliance_class_name, "TestFailedDataComplianceRule")
|
|
68
73
|
self.assertEqual(result.validated_object, self.s)
|
|
@@ -71,10 +76,10 @@ class TestCompliance(TestCase):
|
|
|
71
76
|
|
|
72
77
|
def test_validate_replaces_results(self):
|
|
73
78
|
self.assertEqual(
|
|
74
|
-
len(DataCompliance.objects.filter(compliance_class_name=TestFailedDataComplianceRule.__name__)),
|
|
79
|
+
len(DataCompliance.objects.filter(compliance_class_name=TestFailedDataComplianceRule.__name__)), 4
|
|
75
80
|
)
|
|
76
81
|
TestFailedDataComplianceRule(self.s).clean()
|
|
77
82
|
self.assertEqual(
|
|
78
83
|
len(DataCompliance.objects.filter(compliance_class_name=TestFailedDataComplianceRule.__name__)),
|
|
79
|
-
|
|
84
|
+
4,
|
|
80
85
|
)
|
|
@@ -40,9 +40,7 @@ class ValidationRuleFilterTestCaseMixin(ValidationRuleTestCaseMixin):
|
|
|
40
40
|
self.assertQuerysetEqualAndNotEmpty(self.filterset(params, self.queryset).qs, expected_queryset)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
class RegularExpressionValidationRuleFilterTestCase(
|
|
44
|
-
ValidationRuleFilterTestCaseMixin, FilterTestCases.NameOnlyFilterTestCase
|
|
45
|
-
):
|
|
43
|
+
class RegularExpressionValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.FilterTestCase):
|
|
46
44
|
"""
|
|
47
45
|
Filterset test cases for the RegularExpressionValidationRule model
|
|
48
46
|
"""
|
|
@@ -52,6 +50,7 @@ class RegularExpressionValidationRuleFilterTestCase(
|
|
|
52
50
|
filterset = RegularExpressionValidationRuleFilterSet
|
|
53
51
|
generic_filter_tests = [
|
|
54
52
|
("id",),
|
|
53
|
+
("name",),
|
|
55
54
|
("regular_expression",),
|
|
56
55
|
("error_message",),
|
|
57
56
|
("field",),
|
|
@@ -91,7 +90,7 @@ class RegularExpressionValidationRuleFilterTestCase(
|
|
|
91
90
|
)
|
|
92
91
|
|
|
93
92
|
|
|
94
|
-
class MinMaxValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.
|
|
93
|
+
class MinMaxValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.FilterTestCase):
|
|
95
94
|
"""
|
|
96
95
|
Filterset test cases for the MinMaxValidationRule model
|
|
97
96
|
"""
|
|
@@ -101,6 +100,7 @@ class MinMaxValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, Filt
|
|
|
101
100
|
filterset = MinMaxValidationRuleFilterSet
|
|
102
101
|
generic_filter_tests = [
|
|
103
102
|
("id",),
|
|
103
|
+
("name",),
|
|
104
104
|
("error_message",),
|
|
105
105
|
("field",),
|
|
106
106
|
]
|
|
@@ -144,7 +144,7 @@ class MinMaxValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, Filt
|
|
|
144
144
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
class RequiredValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.
|
|
147
|
+
class RequiredValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.FilterTestCase):
|
|
148
148
|
"""
|
|
149
149
|
Filterset test cases for the RequiredValidationRule model
|
|
150
150
|
"""
|
|
@@ -154,6 +154,7 @@ class RequiredValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, Fi
|
|
|
154
154
|
filterset = RequiredValidationRuleFilterSet
|
|
155
155
|
generic_filter_tests = [
|
|
156
156
|
("id",),
|
|
157
|
+
("name",),
|
|
157
158
|
("error_message",),
|
|
158
159
|
("field",),
|
|
159
160
|
]
|
|
@@ -189,7 +190,7 @@ class RequiredValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, Fi
|
|
|
189
190
|
)
|
|
190
191
|
|
|
191
192
|
|
|
192
|
-
class UniqueValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.
|
|
193
|
+
class UniqueValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, FilterTestCases.FilterTestCase):
|
|
193
194
|
"""
|
|
194
195
|
Filterset test cases for the UniqueValidationRule model
|
|
195
196
|
"""
|
|
@@ -199,6 +200,7 @@ class UniqueValidationRuleFilterTestCase(ValidationRuleFilterTestCaseMixin, Filt
|
|
|
199
200
|
filterset = UniqueValidationRuleFilterSet
|
|
200
201
|
generic_filter_tests = [
|
|
201
202
|
("id",),
|
|
203
|
+
("name",),
|
|
202
204
|
("error_message",),
|
|
203
205
|
("field",),
|
|
204
206
|
("max_instances",),
|
|
@@ -3,6 +3,7 @@ Model test cases
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
from unittest import TestCase
|
|
6
7
|
|
|
7
8
|
from django.contrib.contenttypes.models import ContentType
|
|
8
9
|
from django.core.validators import ValidationError
|
|
@@ -376,3 +377,17 @@ class UniqueValidationRuleModelTestCase(ValidationRuleModelTestCases.ValidationR
|
|
|
376
377
|
)
|
|
377
378
|
|
|
378
379
|
rule.clean()
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class ValidationRuleModelMixinTestCase(TestCase):
|
|
383
|
+
"""
|
|
384
|
+
Validate ValidationRuleModelMixin is working as intended.
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
def test_is_data_compliance_model(self):
|
|
388
|
+
"""Validate is_data_compliance_model is set correctly on models using ValidationRuleModelMixin."""
|
|
389
|
+
# These models should have is_data_compliance_model = False
|
|
390
|
+
self.assertFalse(MinMaxValidationRule.is_data_compliance_model)
|
|
391
|
+
self.assertFalse(RegularExpressionValidationRule.is_data_compliance_model)
|
|
392
|
+
self.assertFalse(RequiredValidationRule.is_data_compliance_model)
|
|
393
|
+
self.assertFalse(UniqueValidationRule.is_data_compliance_model)
|