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
nautobot/extras/api/views.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
2
|
from django.contrib.contenttypes.models import ContentType
|
|
3
|
+
from django.db import transaction
|
|
3
4
|
from django.db.models import ProtectedError
|
|
4
5
|
from django.forms import ValidationError as FormsValidationError
|
|
5
6
|
from django.http import FileResponse, Http404
|
|
@@ -897,55 +898,61 @@ class JobViewSetBase(
|
|
|
897
898
|
if schedule_data is None:
|
|
898
899
|
schedule_data = {"interval": JobExecutionType.TYPE_IMMEDIATELY, "start_time": timezone.now()}
|
|
899
900
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
request.user,
|
|
903
|
-
name=schedule_data.get("name"),
|
|
904
|
-
start_time=schedule_data.get("start_time"),
|
|
905
|
-
interval=schedule_data.get("interval"),
|
|
906
|
-
crontab=schedule_data.get("crontab", ""),
|
|
907
|
-
job_queue=job_queue,
|
|
908
|
-
validated_save=False,
|
|
909
|
-
**job_class.serialize_data(cleaned_data),
|
|
910
|
-
)
|
|
911
|
-
|
|
912
|
-
scheduled_job_has_approval_workflow = schedule.has_approval_workflow_definition()
|
|
913
|
-
if job_model.has_sensitive_variables:
|
|
914
|
-
if (
|
|
915
|
-
"schedule" in request.data
|
|
916
|
-
and "interval" in request.data["schedule"]
|
|
917
|
-
and request.data["schedule"]["interval"] != JobExecutionType.TYPE_IMMEDIATELY
|
|
918
|
-
):
|
|
919
|
-
raise ValidationError(
|
|
920
|
-
{"schedule": {"interval": ["Unable to schedule job: Job may have sensitive input variables"]}}
|
|
921
|
-
)
|
|
922
|
-
# check approval_required pointer
|
|
923
|
-
if scheduled_job_has_approval_workflow:
|
|
924
|
-
raise ValidationError(
|
|
925
|
-
"Unable to run or schedule job: "
|
|
926
|
-
"This job is flagged as possibly having sensitive variables but also has an applicable approval workflow definition."
|
|
927
|
-
"Modify or remove the approval workflow definition or modify the job to set `has_sensitive_variables` to False."
|
|
928
|
-
)
|
|
929
|
-
|
|
930
|
-
# Approval is not required for dryrun
|
|
931
|
-
# TODO: remove this once we have the ability to configure an approval workflow to ignore jobs with specific parameters(including `dryrun`)
|
|
932
|
-
dryrun = data.get("dryrun", False) if job_class.supports_dryrun else False
|
|
933
|
-
|
|
934
|
-
if (not dryrun and scheduled_job_has_approval_workflow) or schedule_data[
|
|
935
|
-
"interval"
|
|
936
|
-
] in JobExecutionType.SCHEDULE_CHOICES:
|
|
937
|
-
schedule.validated_save()
|
|
938
|
-
serializer = serializers.ScheduledJobSerializer(schedule, context={"request": request})
|
|
939
|
-
return Response({"scheduled_job": serializer.data, "job_result": None}, status=status.HTTP_201_CREATED)
|
|
940
|
-
else:
|
|
941
|
-
job_result = JobResult.enqueue_job(
|
|
901
|
+
with transaction.atomic():
|
|
902
|
+
schedule = ScheduledJob.create_schedule(
|
|
942
903
|
job_model,
|
|
943
904
|
request.user,
|
|
905
|
+
name=schedule_data.get("name"),
|
|
906
|
+
start_time=schedule_data.get("start_time"),
|
|
907
|
+
interval=schedule_data.get("interval"),
|
|
908
|
+
crontab=schedule_data.get("crontab", ""),
|
|
944
909
|
job_queue=job_queue,
|
|
945
910
|
**job_class.serialize_data(cleaned_data),
|
|
946
911
|
)
|
|
947
|
-
|
|
948
|
-
|
|
912
|
+
|
|
913
|
+
scheduled_job_has_approval_workflow = schedule.has_approval_workflow_definition()
|
|
914
|
+
if job_model.has_sensitive_variables:
|
|
915
|
+
if (
|
|
916
|
+
"schedule" in request.data
|
|
917
|
+
and "interval" in request.data["schedule"]
|
|
918
|
+
and request.data["schedule"]["interval"] != JobExecutionType.TYPE_IMMEDIATELY
|
|
919
|
+
):
|
|
920
|
+
schedule.delete()
|
|
921
|
+
schedule = None
|
|
922
|
+
raise ValidationError(
|
|
923
|
+
{"schedule": {"interval": ["Unable to schedule job: Job may have sensitive input variables"]}}
|
|
924
|
+
)
|
|
925
|
+
# check approval_required pointer
|
|
926
|
+
if scheduled_job_has_approval_workflow:
|
|
927
|
+
schedule.delete()
|
|
928
|
+
schedule = None
|
|
929
|
+
raise ValidationError(
|
|
930
|
+
"Unable to run or schedule job: "
|
|
931
|
+
"This job is flagged as possibly having sensitive variables but also has an applicable approval workflow definition."
|
|
932
|
+
"Modify or remove the approval workflow definition or modify the job to set `has_sensitive_variables` to False."
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
# Approval is not required for dryrun
|
|
936
|
+
# TODO: remove this once we have the ability to configure an approval workflow to ignore jobs with specific parameters(including `dryrun`)
|
|
937
|
+
dryrun = data.get("dryrun", False) if job_class.supports_dryrun else False
|
|
938
|
+
|
|
939
|
+
if (not dryrun and scheduled_job_has_approval_workflow) or schedule_data[
|
|
940
|
+
"interval"
|
|
941
|
+
] in JobExecutionType.SCHEDULE_CHOICES:
|
|
942
|
+
serializer = serializers.ScheduledJobSerializer(schedule, context={"request": request})
|
|
943
|
+
return Response({"scheduled_job": serializer.data, "job_result": None}, status=status.HTTP_201_CREATED)
|
|
944
|
+
|
|
945
|
+
schedule.delete()
|
|
946
|
+
schedule = None
|
|
947
|
+
|
|
948
|
+
job_result = JobResult.enqueue_job(
|
|
949
|
+
job_model,
|
|
950
|
+
request.user,
|
|
951
|
+
job_queue=job_queue,
|
|
952
|
+
**job_class.serialize_data(cleaned_data),
|
|
953
|
+
)
|
|
954
|
+
serializer = serializers.JobResultSerializer(job_result, context={"request": request})
|
|
955
|
+
return Response({"scheduled_job": None, "job_result": serializer.data}, status=status.HTTP_201_CREATED)
|
|
949
956
|
|
|
950
957
|
|
|
951
958
|
class JobViewSet(
|
|
@@ -1093,7 +1100,15 @@ class JobButtonViewSet(NotesViewSetMixin, ModelViewSet):
|
|
|
1093
1100
|
#
|
|
1094
1101
|
|
|
1095
1102
|
|
|
1096
|
-
class ScheduledJobViewSet(
|
|
1103
|
+
class ScheduledJobViewSet(
|
|
1104
|
+
# DRF mixins:
|
|
1105
|
+
# note no CreateModelMixin or UpdateModelMixin
|
|
1106
|
+
mixins.DestroyModelMixin,
|
|
1107
|
+
# Nautobot mixins:
|
|
1108
|
+
BulkDestroyModelMixin,
|
|
1109
|
+
# Base class
|
|
1110
|
+
ReadOnlyModelViewSet,
|
|
1111
|
+
):
|
|
1097
1112
|
"""
|
|
1098
1113
|
Retrieve a list of scheduled jobs
|
|
1099
1114
|
"""
|
nautobot/extras/choices.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from celery import states
|
|
2
2
|
|
|
3
3
|
from nautobot.core.choices import ChoiceSet
|
|
4
|
-
from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
|
|
5
4
|
|
|
6
5
|
#
|
|
7
6
|
# Approval Workflows
|
|
@@ -159,7 +158,7 @@ class CustomFieldTypeChoices(ChoiceSet):
|
|
|
159
158
|
|
|
160
159
|
|
|
161
160
|
class ButtonClassChoices(ChoiceSet):
|
|
162
|
-
CLASS_DEFAULT = "default"
|
|
161
|
+
CLASS_DEFAULT = "default" # maps to "secondary" in v3 UI, but kept for backwards compatibility
|
|
163
162
|
CLASS_PRIMARY = "primary"
|
|
164
163
|
CLASS_SUCCESS = "success"
|
|
165
164
|
CLASS_INFO = "info"
|
|
@@ -171,23 +170,13 @@ class ButtonClassChoices(ChoiceSet):
|
|
|
171
170
|
(CLASS_DEFAULT, "Default"),
|
|
172
171
|
(CLASS_PRIMARY, "Primary (blue)"),
|
|
173
172
|
(CLASS_SUCCESS, "Success (green)"),
|
|
174
|
-
(CLASS_INFO, "Info (
|
|
173
|
+
(CLASS_INFO, "Info (blue)"),
|
|
175
174
|
(CLASS_WARNING, "Warning (orange)"),
|
|
176
175
|
(CLASS_DANGER, "Danger (red)"),
|
|
177
176
|
(CLASS_LINK, "None (link)"),
|
|
178
177
|
)
|
|
179
178
|
|
|
180
179
|
|
|
181
|
-
#
|
|
182
|
-
# CustomLinks
|
|
183
|
-
#
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
@class_deprecated_in_favor_of(ButtonClassChoices)
|
|
187
|
-
class CustomLinkButtonClassChoices(ButtonClassChoices):
|
|
188
|
-
pass
|
|
189
|
-
|
|
190
|
-
|
|
191
180
|
#
|
|
192
181
|
# Dynamic Groups
|
|
193
182
|
#
|
|
@@ -18,7 +18,7 @@ import yaml
|
|
|
18
18
|
|
|
19
19
|
from nautobot.core.utils.git import GitRepo
|
|
20
20
|
from nautobot.core.utils.module_loading import check_name_safe_to_import_privately, import_modules_privately
|
|
21
|
-
from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
21
|
+
from nautobot.dcim.models import Device, DeviceFamily, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
22
22
|
from nautobot.extras.choices import (
|
|
23
23
|
LogLevelChoices,
|
|
24
24
|
SecretsGroupAccessTypeChoices,
|
|
@@ -274,6 +274,7 @@ def update_git_config_contexts(repository_record, job_result):
|
|
|
274
274
|
for filter_type in (
|
|
275
275
|
"locations",
|
|
276
276
|
"device_types",
|
|
277
|
+
"device_families",
|
|
277
278
|
"roles",
|
|
278
279
|
"platforms",
|
|
279
280
|
"cluster_groups",
|
|
@@ -408,6 +409,7 @@ def import_config_context(context_data, repository_record, job_result):
|
|
|
408
409
|
for key, model_class in [
|
|
409
410
|
("locations", Location),
|
|
410
411
|
("device_types", DeviceType),
|
|
412
|
+
("device_families", DeviceFamily),
|
|
411
413
|
("roles", Role),
|
|
412
414
|
("platforms", Platform),
|
|
413
415
|
("cluster_groups", ClusterGroup),
|
|
@@ -21,7 +21,7 @@ from nautobot.extras.choices import (
|
|
|
21
21
|
CustomFieldTypeChoices,
|
|
22
22
|
RelationshipSideChoices,
|
|
23
23
|
)
|
|
24
|
-
from nautobot.extras.
|
|
24
|
+
from nautobot.extras.filter_mixins_customfields import (
|
|
25
25
|
CustomFieldBooleanFilter,
|
|
26
26
|
CustomFieldCharFilter,
|
|
27
27
|
CustomFieldDateFilter,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from functools import reduce
|
|
2
|
+
import operator
|
|
3
|
+
|
|
1
4
|
from django.db.models import Q
|
|
2
5
|
from django.forms import IntegerField
|
|
3
6
|
import django_filters
|
|
@@ -36,19 +39,52 @@ class CustomFieldFilterMixin:
|
|
|
36
39
|
super().__init__(*args, **kwargs)
|
|
37
40
|
self.field_name = f"_custom_field_data__{self.field_name}"
|
|
38
41
|
|
|
42
|
+
def generate_query(self, value):
|
|
43
|
+
# This method may be called from extras.models.DynamicGroup._generate_query_for_filter method
|
|
44
|
+
# to generate proper query for given field. But at this point, we don't know if the field will be negated or not
|
|
45
|
+
# That's why we're preparing query that works both: for positional filtering and negated one.
|
|
46
|
+
# For positional filtering, when we're expecting some value, the field must exists (key in custom field data JSONB)
|
|
47
|
+
# and value can't be None (null in db)
|
|
48
|
+
# For negated filtering we're expecting field without some value, but key may be missing or value can be None (null in db)
|
|
49
|
+
# Please refer to the filter method below, for more context.
|
|
50
|
+
if value == "null" or value == ["null"]:
|
|
51
|
+
return Q(**{f"{self.field_name}__exact": None}) & Q(**{f"{self.field_name}__isnull": False})
|
|
52
|
+
|
|
53
|
+
if isinstance(value, (list, tuple)):
|
|
54
|
+
value_match = reduce(operator.or_, [Q(**{f"{self.field_name}__{self.lookup_expr}": v}) for v in value])
|
|
55
|
+
else:
|
|
56
|
+
value_match = Q(**{f"{self.field_name}__{self.lookup_expr}": value})
|
|
57
|
+
value_is_not_none = ~Q(**{f"{self.field_name}__exact": None})
|
|
58
|
+
key_is_present_in_jsonb = Q(
|
|
59
|
+
**{f"{self.field_name}__isnull": False}
|
|
60
|
+
) # __isnull and __has_key has same output in case of JSONB fields
|
|
61
|
+
|
|
62
|
+
return value_match & value_is_not_none & key_is_present_in_jsonb
|
|
63
|
+
|
|
39
64
|
def filter(self, qs, value):
|
|
40
65
|
if value in django_filters.constants.EMPTY_VALUES:
|
|
41
66
|
return qs
|
|
42
67
|
|
|
43
|
-
if value == "null":
|
|
68
|
+
if value == "null" or value == ["null"]:
|
|
44
69
|
return self.get_method(qs)(
|
|
45
|
-
Q(**{f"{self.field_name}__exact": None})
|
|
70
|
+
Q(**{f"{self.field_name}__exact": None}) & Q(**{f"{self.field_name}__isnull": False})
|
|
46
71
|
)
|
|
47
72
|
|
|
48
73
|
# Custom fields require special handling for exclude filtering.
|
|
49
|
-
# Return custom fields that don't match the value
|
|
74
|
+
# Return custom fields that don't match the value, key is missing or value is set to null
|
|
75
|
+
# For JSONB fields, like `_custom_field_data`:
|
|
76
|
+
# __isnull and __has_key returns those records which has key
|
|
77
|
+
# __isnull is not checking the actual value in JSONB!
|
|
78
|
+
# to check for null value, we need to use exact
|
|
79
|
+
# With exclude filtering we need to take into account all cases:
|
|
80
|
+
# - no key - handled by __isnull check
|
|
81
|
+
# - key is present with null - handled by __exact=None
|
|
82
|
+
# - key is present with some value - handled by filter
|
|
83
|
+
# - key is present with empty str - handled by filter
|
|
50
84
|
if self.exclude:
|
|
51
|
-
qs_null_custom_fields = qs.filter(
|
|
85
|
+
qs_null_custom_fields = qs.filter(
|
|
86
|
+
Q(**{f"{self.field_name}__isnull": True}) | Q(**{f"{self.field_name}__exact": None})
|
|
87
|
+
).distinct()
|
|
52
88
|
return super().filter(qs, value).distinct() | qs_null_custom_fields
|
|
53
89
|
|
|
54
90
|
return super().filter(qs, value)
|
|
@@ -58,7 +94,7 @@ class CustomFieldBooleanFilter(CustomFieldFilterMixin, django_filters.BooleanFil
|
|
|
58
94
|
"""Custom field single value filter for backwards compatibility"""
|
|
59
95
|
|
|
60
96
|
|
|
61
|
-
class CustomFieldCharFilter(CustomFieldFilterMixin, django_filters.
|
|
97
|
+
class CustomFieldCharFilter(CustomFieldFilterMixin, django_filters.CharFilter):
|
|
62
98
|
"""Custom field single value filter for backwards compatibility"""
|
|
63
99
|
|
|
64
100
|
|
|
@@ -92,7 +128,7 @@ class CustomFieldMultiSelectFilter(CustomFieldSelectFilter):
|
|
|
92
128
|
super().__init__(*args, **kwargs)
|
|
93
129
|
|
|
94
130
|
|
|
95
|
-
class CustomFieldNumberFilter(CustomFieldFilterMixin, django_filters.
|
|
131
|
+
class CustomFieldNumberFilter(CustomFieldFilterMixin, django_filters.NumberFilter):
|
|
96
132
|
"""Custom field single value filter for backwards compatibility"""
|
|
97
133
|
|
|
98
134
|
field_class = IntegerField
|
|
@@ -23,9 +23,9 @@ from nautobot.core.filters import (
|
|
|
23
23
|
RelatedMembershipBooleanFilter,
|
|
24
24
|
SearchFilter,
|
|
25
25
|
)
|
|
26
|
-
from nautobot.
|
|
27
|
-
from nautobot.dcim.models import DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
26
|
+
from nautobot.dcim.models import DeviceFamily, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
28
27
|
from nautobot.extras.choices import (
|
|
28
|
+
ApprovalWorkflowStateChoices,
|
|
29
29
|
JobQueueTypeChoices,
|
|
30
30
|
JobResultStatusChoices,
|
|
31
31
|
MetadataTypeDataTypeChoices,
|
|
@@ -33,7 +33,18 @@ from nautobot.extras.choices import (
|
|
|
33
33
|
SecretsGroupAccessTypeChoices,
|
|
34
34
|
SecretsGroupSecretTypeChoices,
|
|
35
35
|
)
|
|
36
|
-
from nautobot.extras.
|
|
36
|
+
from nautobot.extras.filter_mixins import (
|
|
37
|
+
ConfigContextRoleFilter,
|
|
38
|
+
CreatedUpdatedModelFilterSetMixin,
|
|
39
|
+
CustomFieldModelFilterSetMixin,
|
|
40
|
+
LocalContextModelFilterSetMixin,
|
|
41
|
+
RelationshipFilter,
|
|
42
|
+
RelationshipModelFilterSetMixin,
|
|
43
|
+
RoleModelFilterSetMixin,
|
|
44
|
+
StatusFilter,
|
|
45
|
+
StatusModelFilterSetMixin,
|
|
46
|
+
)
|
|
47
|
+
from nautobot.extras.filter_mixins_customfields import (
|
|
37
48
|
CustomFieldBooleanFilter,
|
|
38
49
|
CustomFieldCharFilter,
|
|
39
50
|
CustomFieldDateFilter,
|
|
@@ -45,17 +56,6 @@ from nautobot.extras.filters.customfields import (
|
|
|
45
56
|
CustomFieldMultiValueNumberFilter,
|
|
46
57
|
CustomFieldNumberFilter,
|
|
47
58
|
)
|
|
48
|
-
from nautobot.extras.filters.mixins import (
|
|
49
|
-
ConfigContextRoleFilter,
|
|
50
|
-
CreatedUpdatedModelFilterSetMixin,
|
|
51
|
-
CustomFieldModelFilterSetMixin,
|
|
52
|
-
LocalContextModelFilterSetMixin,
|
|
53
|
-
RelationshipFilter,
|
|
54
|
-
RelationshipModelFilterSetMixin,
|
|
55
|
-
RoleModelFilterSetMixin,
|
|
56
|
-
StatusFilter,
|
|
57
|
-
StatusModelFilterSetMixin,
|
|
58
|
-
)
|
|
59
59
|
from nautobot.extras.models import (
|
|
60
60
|
ApprovalWorkflow,
|
|
61
61
|
ApprovalWorkflowDefinition,
|
|
@@ -126,14 +126,12 @@ __all__ = (
|
|
|
126
126
|
"ContactFilterSet",
|
|
127
127
|
"ContentTypeFilterSet",
|
|
128
128
|
"ContentTypeMultipleChoiceFilter",
|
|
129
|
-
"CreatedUpdatedFilterSet",
|
|
130
129
|
"CreatedUpdatedModelFilterSetMixin",
|
|
131
130
|
"CustomFieldBooleanFilter",
|
|
132
131
|
"CustomFieldCharFilter",
|
|
133
132
|
"CustomFieldDateFilter",
|
|
134
133
|
"CustomFieldFilterMixin",
|
|
135
134
|
"CustomFieldJSONFilter",
|
|
136
|
-
"CustomFieldModelFilterSet",
|
|
137
135
|
"CustomFieldModelFilterSetMixin",
|
|
138
136
|
"CustomFieldMultiSelectFilter",
|
|
139
137
|
"CustomFieldMultiValueCharFilter",
|
|
@@ -153,7 +151,6 @@ __all__ = (
|
|
|
153
151
|
"JobQueueAssignmentFilterSet",
|
|
154
152
|
"JobQueueFilterSet",
|
|
155
153
|
"JobResultFilterSet",
|
|
156
|
-
"LocalContextFilterSet",
|
|
157
154
|
"LocalContextModelFilterSetMixin",
|
|
158
155
|
"MetadataChoiceFilterSet",
|
|
159
156
|
"MetadataTypeFilterSet",
|
|
@@ -178,17 +175,6 @@ __all__ = (
|
|
|
178
175
|
)
|
|
179
176
|
|
|
180
177
|
|
|
181
|
-
# TODO: remove in 2.2
|
|
182
|
-
@class_deprecated_in_favor_of(CreatedUpdatedModelFilterSetMixin)
|
|
183
|
-
class CreatedUpdatedFilterSet(CreatedUpdatedModelFilterSetMixin):
|
|
184
|
-
pass
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
@class_deprecated_in_favor_of(RelationshipModelFilterSetMixin)
|
|
188
|
-
class RelationshipModelFilterSet(RelationshipModelFilterSetMixin):
|
|
189
|
-
pass
|
|
190
|
-
|
|
191
|
-
|
|
192
178
|
#
|
|
193
179
|
# Approval Workflows
|
|
194
180
|
#
|
|
@@ -199,7 +185,7 @@ class ApprovalWorkflowStageDefinitionFilterSet(BaseFilterSet):
|
|
|
199
185
|
|
|
200
186
|
q = SearchFilter(
|
|
201
187
|
filter_predicates={
|
|
202
|
-
"
|
|
188
|
+
"sequence": {
|
|
203
189
|
"lookup_expr": "exact",
|
|
204
190
|
"preprocessor": int,
|
|
205
191
|
},
|
|
@@ -401,6 +387,12 @@ class ConfigContextFilterSet(BaseFilterSet):
|
|
|
401
387
|
to_field_name="model",
|
|
402
388
|
label="Device Type (model or ID)",
|
|
403
389
|
)
|
|
390
|
+
device_family = NaturalKeyOrPKMultipleChoiceFilter(
|
|
391
|
+
field_name="device_families",
|
|
392
|
+
queryset=DeviceFamily.objects.all(),
|
|
393
|
+
to_field_name="name",
|
|
394
|
+
label="Device Family (name or ID)",
|
|
395
|
+
)
|
|
404
396
|
platform_id = ModelMultipleChoiceFilter(
|
|
405
397
|
field_name="platforms",
|
|
406
398
|
queryset=Platform.objects.all(),
|
|
@@ -572,12 +564,6 @@ class ContentTypeFilterSet(BaseFilterSet):
|
|
|
572
564
|
return queryset.filter(FeatureQuery(value).get_query())
|
|
573
565
|
|
|
574
566
|
|
|
575
|
-
# TODO: remove in 2.2
|
|
576
|
-
@class_deprecated_in_favor_of(CustomFieldModelFilterSetMixin)
|
|
577
|
-
class CustomFieldModelFilterSet(CustomFieldModelFilterSetMixin):
|
|
578
|
-
pass
|
|
579
|
-
|
|
580
|
-
|
|
581
567
|
class CustomFieldFilterSet(BaseFilterSet):
|
|
582
568
|
q = SearchFilter(
|
|
583
569
|
filter_predicates={
|
|
@@ -787,7 +773,7 @@ class CustomLinkFilterSet(BaseFilterSet):
|
|
|
787
773
|
#
|
|
788
774
|
|
|
789
775
|
# Must be imported **after* NautobotFilterSet class is defined to avoid a circular import loop.
|
|
790
|
-
from nautobot.tenancy.
|
|
776
|
+
from nautobot.tenancy.filter_mixins import TenancyModelFilterSetMixin # noqa: E402
|
|
791
777
|
|
|
792
778
|
|
|
793
779
|
class DynamicGroupFilterSet(TenancyModelFilterSetMixin, NautobotFilterSet):
|
|
@@ -1229,10 +1215,15 @@ class ScheduledJobFilterSet(BaseFilterSet):
|
|
|
1229
1215
|
label="Time zone",
|
|
1230
1216
|
null_value="",
|
|
1231
1217
|
)
|
|
1218
|
+
approval_state = django_filters.MultipleChoiceFilter(
|
|
1219
|
+
field_name="associated_approval_workflows__current_state",
|
|
1220
|
+
label="Approval state",
|
|
1221
|
+
choices=ApprovalWorkflowStateChoices,
|
|
1222
|
+
)
|
|
1232
1223
|
|
|
1233
1224
|
class Meta:
|
|
1234
1225
|
model = ScheduledJob
|
|
1235
|
-
fields = ["id", "name", "total_run_count", "start_time", "last_run_at", "time_zone"]
|
|
1226
|
+
fields = ["id", "name", "enabled", "total_run_count", "start_time", "last_run_at", "time_zone"]
|
|
1236
1227
|
|
|
1237
1228
|
|
|
1238
1229
|
#
|
|
@@ -1270,17 +1261,6 @@ class JobButtonFilterSet(BaseFilterSet):
|
|
|
1270
1261
|
)
|
|
1271
1262
|
|
|
1272
1263
|
|
|
1273
|
-
#
|
|
1274
|
-
# Filter for Local Config Context Data
|
|
1275
|
-
#
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
# TODO: remove in 2.2
|
|
1279
|
-
@class_deprecated_in_favor_of(LocalContextModelFilterSetMixin)
|
|
1280
|
-
class LocalContextFilterSet(LocalContextModelFilterSetMixin):
|
|
1281
|
-
pass
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
1264
|
#
|
|
1285
1265
|
# Metadata
|
|
1286
1266
|
#
|
|
@@ -1503,6 +1483,11 @@ class SecretFilterSet(
|
|
|
1503
1483
|
"name": "icontains",
|
|
1504
1484
|
},
|
|
1505
1485
|
)
|
|
1486
|
+
secrets_groups = NaturalKeyOrPKMultipleChoiceFilter(
|
|
1487
|
+
queryset=SecretsGroup.objects.all(),
|
|
1488
|
+
label="Groups (ID or name)",
|
|
1489
|
+
to_field_name="name",
|
|
1490
|
+
)
|
|
1506
1491
|
# TODO(Glenn): dynamic choices needed. The issue being that secrets providers are Python
|
|
1507
1492
|
# classes, not database models.
|
|
1508
1493
|
# provider = django_filters.MultipleChoiceFilter(choices=..., null_value=None)
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -42,8 +42,7 @@ from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
|
|
|
42
42
|
from nautobot.core.forms.fields import MultiValueCharField
|
|
43
43
|
from nautobot.core.forms.forms import ConfirmationForm
|
|
44
44
|
from nautobot.core.forms.widgets import ClearableFileInput
|
|
45
|
-
from nautobot.
|
|
46
|
-
from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
45
|
+
from nautobot.dcim.models import Device, DeviceFamily, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
47
46
|
from nautobot.extras.choices import (
|
|
48
47
|
ApprovalWorkflowStateChoices,
|
|
49
48
|
ButtonClassChoices,
|
|
@@ -153,7 +152,6 @@ __all__ = (
|
|
|
153
152
|
"ConfigContextSchemaBulkEditForm",
|
|
154
153
|
"ConfigContextSchemaFilterForm",
|
|
155
154
|
"ConfigContextSchemaForm",
|
|
156
|
-
"CustomFieldBulkCreateForm", # 2.0 TODO remove this deprecated class
|
|
157
155
|
"CustomFieldBulkDeleteForm",
|
|
158
156
|
"CustomFieldBulkEditForm",
|
|
159
157
|
"CustomFieldChoiceFormSet",
|
|
@@ -338,7 +336,7 @@ ApprovalWorkflowStageDefinitionFormSet = inlineformset_factory(
|
|
|
338
336
|
extra=5,
|
|
339
337
|
widgets={
|
|
340
338
|
"name": forms.TextInput(attrs={"class": "form-control"}),
|
|
341
|
-
"
|
|
339
|
+
"sequence": forms.NumberInput(attrs={"class": "form-control"}),
|
|
342
340
|
"min_approvers": forms.NumberInput(attrs={"class": "form-control"}),
|
|
343
341
|
"denial_message": forms.TextInput(attrs={"class": "form-control"}),
|
|
344
342
|
"approver_group": forms.Select(attrs={"class": "form-control"}),
|
|
@@ -352,8 +350,8 @@ class ApprovalWorkflowStageDefinitionBulkEditForm(TagsBulkEditFormMixin, Nautobo
|
|
|
352
350
|
pk = forms.ModelMultipleChoiceField(
|
|
353
351
|
queryset=ApprovalWorkflowStageDefinition.objects.all(), widget=forms.MultipleHiddenInput
|
|
354
352
|
)
|
|
355
|
-
|
|
356
|
-
min_approvers = forms.IntegerField(required=False, label="
|
|
353
|
+
sequence = forms.IntegerField(required=False, label="Sequence")
|
|
354
|
+
min_approvers = forms.IntegerField(required=False, label="Minimum Approvers")
|
|
357
355
|
denial_message = forms.CharField(required=False, label="Denial Message")
|
|
358
356
|
|
|
359
357
|
class Meta:
|
|
@@ -374,8 +372,8 @@ class ApprovalWorkflowStageDefinitionFilterForm(NautobotFilterForm):
|
|
|
374
372
|
required=False,
|
|
375
373
|
label="Approval Workflow Definition",
|
|
376
374
|
)
|
|
377
|
-
|
|
378
|
-
min_approvers = forms.IntegerField(required=False, label="
|
|
375
|
+
sequence = forms.IntegerField(required=False, label="Sequence")
|
|
376
|
+
min_approvers = forms.IntegerField(required=False, label="Minimum Approvers")
|
|
379
377
|
approver_group = DynamicModelChoiceField(
|
|
380
378
|
queryset=Group.objects.all(),
|
|
381
379
|
required=False,
|
|
@@ -546,6 +544,7 @@ class ConfigContextForm(BootstrapMixin, NoteModelFormMixin, forms.ModelForm):
|
|
|
546
544
|
required=False,
|
|
547
545
|
)
|
|
548
546
|
device_types = DynamicModelMultipleChoiceField(queryset=DeviceType.objects.all(), required=False)
|
|
547
|
+
device_families = DynamicModelMultipleChoiceField(queryset=DeviceFamily.objects.all(), required=False)
|
|
549
548
|
platforms = DynamicModelMultipleChoiceField(queryset=Platform.objects.all(), required=False)
|
|
550
549
|
cluster_groups = DynamicModelMultipleChoiceField(queryset=ClusterGroup.objects.all(), required=False)
|
|
551
550
|
clusters = DynamicModelMultipleChoiceField(queryset=Cluster.objects.all(), required=False)
|
|
@@ -578,6 +577,7 @@ class ConfigContextForm(BootstrapMixin, NoteModelFormMixin, forms.ModelForm):
|
|
|
578
577
|
"locations",
|
|
579
578
|
"roles",
|
|
580
579
|
"device_types",
|
|
580
|
+
"device_families",
|
|
581
581
|
"platforms",
|
|
582
582
|
"cluster_groups",
|
|
583
583
|
"clusters",
|
|
@@ -611,7 +611,12 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|
|
611
611
|
role = DynamicModelMultipleChoiceField(
|
|
612
612
|
queryset=Role.objects.get_for_models([Device, VirtualMachine]), to_field_name="name", required=False
|
|
613
613
|
)
|
|
614
|
-
|
|
614
|
+
device_type = DynamicModelMultipleChoiceField(
|
|
615
|
+
queryset=DeviceType.objects.all(), to_field_name="model", required=False
|
|
616
|
+
)
|
|
617
|
+
device_family = DynamicModelMultipleChoiceField(
|
|
618
|
+
queryset=DeviceFamily.objects.all(), to_field_name="name", required=False
|
|
619
|
+
)
|
|
615
620
|
platform = DynamicModelMultipleChoiceField(queryset=Platform.objects.all(), to_field_name="name", required=False)
|
|
616
621
|
cluster_group = DynamicModelMultipleChoiceField(
|
|
617
622
|
queryset=ClusterGroup.objects.all(), to_field_name="name", required=False
|
|
@@ -827,12 +832,6 @@ class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelFormMixin):
|
|
|
827
832
|
self.custom_fields.append(field_name)
|
|
828
833
|
|
|
829
834
|
|
|
830
|
-
# 2.0 TODO: remove this class
|
|
831
|
-
@class_deprecated_in_favor_of(CustomFieldModelBulkEditFormMixin)
|
|
832
|
-
class CustomFieldBulkCreateForm(CustomFieldModelBulkEditFormMixin):
|
|
833
|
-
"""No longer needed as a separate class - use CustomFieldModelBulkEditFormMixin instead."""
|
|
834
|
-
|
|
835
|
-
|
|
836
835
|
class CustomFieldBulkDeleteForm(ConfirmationForm):
|
|
837
836
|
def __init__(self, *args, delete_all=False, **kwargs):
|
|
838
837
|
super().__init__(*args, **kwargs)
|
nautobot/extras/forms/mixins.py
CHANGED
|
@@ -11,7 +11,6 @@ from nautobot.core.forms import (
|
|
|
11
11
|
DynamicModelChoiceField,
|
|
12
12
|
DynamicModelMultipleChoiceField,
|
|
13
13
|
)
|
|
14
|
-
from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
|
|
15
14
|
from nautobot.extras.choices import (
|
|
16
15
|
DynamicGroupTypeChoices,
|
|
17
16
|
RelationshipSideChoices,
|
|
@@ -48,17 +47,10 @@ __all__ = ( # noqa:RUF022
|
|
|
48
47
|
"StatusModelBulkEditFormMixin",
|
|
49
48
|
"StatusModelFilterFormMixin",
|
|
50
49
|
"TagsBulkEditFormMixin",
|
|
51
|
-
# 2.0 TODO: remove the below deprecated aliases
|
|
52
|
-
"AddRemoveTagsForm",
|
|
53
|
-
"CustomFieldBulkEditForm",
|
|
54
|
-
"CustomFieldModelForm",
|
|
55
|
-
"RelationshipModelForm",
|
|
56
50
|
"RoleModelBulkEditFormMixin",
|
|
57
51
|
"RoleModelFilterFormMixin",
|
|
58
52
|
"RoleNotRequiredModelFormMixin",
|
|
59
53
|
"RoleRequiredModelFormMixin",
|
|
60
|
-
"StatusBulkEditFormMixin",
|
|
61
|
-
"StatusFilterFormMixin",
|
|
62
54
|
)
|
|
63
55
|
|
|
64
56
|
|
|
@@ -854,36 +846,3 @@ class TagsBulkEditFormMixin(BulkEditForm):
|
|
|
854
846
|
query_params={"content_types": self.model._meta.label_lower},
|
|
855
847
|
required=False,
|
|
856
848
|
)
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
# 2.2 TODO: Names below are only for backward compatibility with Nautobot 1.3 and earlier. Remove in 2.2
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
@class_deprecated_in_favor_of(TagsBulkEditFormMixin)
|
|
863
|
-
class AddRemoveTagsForm(TagsBulkEditFormMixin):
|
|
864
|
-
pass
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
@class_deprecated_in_favor_of(CustomFieldModelBulkEditFormMixin)
|
|
868
|
-
class CustomFieldBulkEditForm(CustomFieldModelBulkEditFormMixin):
|
|
869
|
-
pass
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
@class_deprecated_in_favor_of(CustomFieldModelFormMixin)
|
|
873
|
-
class CustomFieldModelForm(CustomFieldModelFormMixin):
|
|
874
|
-
pass
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
@class_deprecated_in_favor_of(RelationshipModelFormMixin)
|
|
878
|
-
class RelationshipModelForm(RelationshipModelFormMixin):
|
|
879
|
-
pass
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
@class_deprecated_in_favor_of(StatusModelBulkEditFormMixin)
|
|
883
|
-
class StatusBulkEditFormMixin(StatusModelBulkEditFormMixin):
|
|
884
|
-
pass
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
@class_deprecated_in_favor_of(StatusModelFilterFormMixin)
|
|
888
|
-
class StatusFilterFormMixin(StatusModelFilterFormMixin):
|
|
889
|
-
pass
|
nautobot/extras/jobs.py
CHANGED
|
@@ -468,6 +468,7 @@ class BaseJob:
|
|
|
468
468
|
label="Profile job execution",
|
|
469
469
|
help_text="Profiles the job execution using cProfile and outputs a report to /tmp/",
|
|
470
470
|
)
|
|
471
|
+
form.fields["_profile"].widget.attrs["class"] = "form-check-input"
|
|
471
472
|
# If the class already exists there may be overrides, so we have to check this.
|
|
472
473
|
try:
|
|
473
474
|
job_model = JobModel.objects.get(module_name=cls.__module__, job_class_name=cls.__name__)
|
|
@@ -484,6 +485,7 @@ class BaseJob:
|
|
|
484
485
|
label="Ignore singleton lock",
|
|
485
486
|
help_text="Allow this singleton job to run even when another instance is already running",
|
|
486
487
|
)
|
|
488
|
+
form.fields["_ignore_singleton_lock"].widget.attrs["class"] = "form-check-input"
|
|
487
489
|
|
|
488
490
|
if job_model is not None:
|
|
489
491
|
job_queue_queryset = JobQueue.objects.filter(jobs=job_model)
|
nautobot/extras/jobs_ui.py
CHANGED
|
@@ -165,10 +165,11 @@ class JobKeyValueOverrideValueTablePanel(KeyValueTablePanel):
|
|
|
165
165
|
else:
|
|
166
166
|
value_tag = format_html(
|
|
167
167
|
"""
|
|
168
|
-
<span
|
|
168
|
+
<span>
|
|
169
169
|
<span id="{unique_id}_value_{key}">{value}</span>
|
|
170
|
-
<button class="btn btn-
|
|
171
|
-
<span class="mdi mdi-content-copy"></span>
|
|
170
|
+
<button class="btn btn-secondary nb-btn-inline-hover" data-clipboard-target="#{unique_id}_value_{key}">
|
|
171
|
+
<span aria-hidden="true" class="mdi mdi-content-copy"></span>
|
|
172
|
+
<span class="visually-hidden">Copy</span>
|
|
172
173
|
</button>
|
|
173
174
|
</span>
|
|
174
175
|
""",
|