nautobot 2.4.21__py3-none-any.whl → 3.0.0a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/choices.py +2 -2
- nautobot/apps/filters.py +9 -9
- nautobot/apps/forms.py +2 -0
- nautobot/apps/models.py +7 -2
- nautobot/apps/ui.py +20 -1
- nautobot/apps/utils.py +2 -3
- nautobot/apps/views.py +7 -1
- nautobot/circuits/filters.py +8 -23
- nautobot/circuits/navigation.py +3 -1
- nautobot/circuits/templates/circuits/circuit_create.html +9 -9
- nautobot/circuits/templates/circuits/circuit_terminations_swap.html +2 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +24 -33
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +10 -10
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +13 -13
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +6 -6
- nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +3 -3
- nautobot/circuits/templates/circuits/inc/speed_widget.html +13 -13
- nautobot/circuits/templates/circuits/provider_create.html +9 -9
- nautobot/circuits/tests/integration/test_circuit.py +19 -19
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +3 -0
- nautobot/circuits/tests/integration/test_relationships.py +4 -12
- nautobot/circuits/views.py +0 -2
- nautobot/cloud/filters.py +1 -13
- nautobot/cloud/navigation.py +3 -1
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +9 -9
- nautobot/cloud/templates/cloud/cloudservice_update.html +6 -6
- nautobot/core/api/fields.py +30 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +9 -2
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +58 -37
- nautobot/core/apps/__init__.py +6 -12
- nautobot/core/branching.py +83 -0
- nautobot/core/celery/__init__.py +11 -6
- nautobot/core/celery/backends.py +2 -0
- nautobot/core/celery/encoders.py +7 -0
- nautobot/core/celery/task.py +44 -0
- nautobot/core/checks.py +60 -0
- nautobot/core/cli/bootstrap_v3_to_v5.py +776 -0
- nautobot/core/constants.py +9 -0
- nautobot/core/context_processors.py +84 -0
- nautobot/core/filters.py +131 -2
- nautobot/core/forms/__init__.py +4 -2
- nautobot/core/forms/fields.py +10 -8
- nautobot/core/forms/forms.py +21 -9
- nautobot/core/forms/search.py +0 -15
- nautobot/core/forms/widgets.py +3 -2
- nautobot/core/graphql/__init__.py +8 -26
- nautobot/core/graphql/generators.py +16 -6
- nautobot/core/graphql/schema.py +1 -1
- nautobot/core/graphql/schema_init.py +1 -2
- nautobot/core/graphql/utils.py +7 -9
- nautobot/core/jobs/__init__.py +158 -0
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +17 -2
- nautobot/core/models/fields.py +3 -2
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/name_color_content_types.py +1 -1
- nautobot/core/models/ordering.py +7 -5
- nautobot/core/models/querysets.py +77 -2
- nautobot/core/models/tree_queries.py +6 -4
- nautobot/core/settings.py +30 -16
- nautobot/core/settings.yaml +13 -7
- nautobot/core/tables.py +114 -44
- nautobot/core/templates/403.html +1 -1
- nautobot/core/templates/403_csrf_failure.html +1 -1
- nautobot/core/templates/404.html +1 -1
- nautobot/core/templates/40x.html +8 -8
- nautobot/core/templates/500.html +10 -10
- nautobot/core/templates/about.html +13 -12
- nautobot/core/templates/admin/actions.html +1 -1
- nautobot/core/templates/admin/app_index.html +3 -3
- nautobot/core/templates/admin/base.html +45 -52
- nautobot/core/templates/admin/base_site.html +0 -9
- nautobot/core/templates/admin/change_form.html +5 -5
- nautobot/core/templates/admin/change_list.html +8 -12
- nautobot/core/templates/admin/change_list_results.html +3 -3
- nautobot/core/templates/admin/config/config.html +24 -24
- nautobot/core/templates/admin/delete_confirmation.html +5 -5
- nautobot/core/templates/admin/edit_inline/stacked.html +5 -5
- nautobot/core/templates/admin/edit_inline/tabular.html +3 -3
- nautobot/core/templates/admin/includes/fieldset.html +15 -15
- nautobot/core/templates/admin/index.html +8 -8
- nautobot/core/templates/admin/submit_line.html +5 -5
- nautobot/core/templates/base_django.html +36 -32
- nautobot/core/templates/buttons/add.html +1 -1
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +2 -2
- nautobot/core/templates/buttons/export.html +17 -18
- nautobot/core/templates/buttons/job_import.html +2 -2
- nautobot/core/templates/components/breadcrumbs.html +19 -17
- nautobot/core/templates/components/button/dropdown.html +7 -5
- nautobot/core/templates/components/echarts.html +2 -0
- nautobot/core/templates/components/layout/one_over_two.html +3 -3
- nautobot/core/templates/components/layout/two_over_one.html +3 -3
- nautobot/core/templates/components/panel/body_content_data_table.html +2 -2
- nautobot/core/templates/components/panel/body_content_tags.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_generic.html +4 -2
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_table.html +4 -2
- nautobot/core/templates/components/panel/footer_contacts_table.html +4 -4
- nautobot/core/templates/components/panel/footer_content_table.html +3 -3
- nautobot/core/templates/components/panel/grouping_toggle.html +12 -11
- nautobot/core/templates/components/panel/header_extra_content_table.html +2 -11
- nautobot/core/templates/components/panel/panel.html +6 -3
- nautobot/core/templates/components/panel/stats_panel_body.html +9 -7
- nautobot/core/templates/components/tab/content_wrapper.html +29 -1
- nautobot/core/templates/components/tab/label_wrapper.html +10 -2
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +11 -4
- nautobot/core/templates/echarts/echarts.html +20 -0
- nautobot/core/templates/exceptions/import_error.html +2 -2
- nautobot/core/templates/exceptions/permission_error.html +1 -1
- nautobot/core/templates/exceptions/programming_error.html +2 -2
- nautobot/core/templates/generic/object_bulk_add_component.html +29 -20
- nautobot/core/templates/generic/object_bulk_create.html +87 -75
- nautobot/core/templates/generic/object_bulk_destroy.html +35 -37
- nautobot/core/templates/generic/object_bulk_remove.html +30 -26
- nautobot/core/templates/generic/object_bulk_rename.html +53 -40
- nautobot/core/templates/generic/object_bulk_update.html +36 -29
- nautobot/core/templates/generic/object_create.html +40 -27
- nautobot/core/templates/generic/object_import.html +36 -24
- nautobot/core/templates/generic/object_list.html +279 -215
- nautobot/core/templates/generic/object_notes.html +21 -11
- nautobot/core/templates/generic/object_retrieve.html +161 -213
- nautobot/core/templates/graphene/graphiql.html +113 -60
- nautobot/core/templates/home.html +164 -87
- nautobot/core/templates/import_success.html +3 -2
- nautobot/core/templates/inc/ajax_loader.html +1 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +25 -13
- nautobot/core/templates/inc/created_updated.html +12 -7
- nautobot/core/templates/inc/custom_fields/panel_data.html +28 -16
- nautobot/core/templates/inc/custom_fields_panel.html +3 -3
- nautobot/core/templates/inc/dynamic_groups_panel.html +3 -3
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +15 -15
- nautobot/core/templates/inc/footer.html +90 -40
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +75 -0
- nautobot/core/templates/inc/header_banners.html +17 -0
- nautobot/core/templates/inc/header_messages.html +6 -0
- nautobot/core/templates/inc/image_attachments.html +9 -9
- nautobot/core/templates/inc/javascript.html +7 -24
- nautobot/core/templates/inc/media.html +4 -29
- nautobot/core/templates/inc/modal.html +2 -2
- nautobot/core/templates/inc/nav_favorites.html +27 -0
- nautobot/core/templates/inc/nav_menu.html +150 -108
- nautobot/core/templates/inc/object_details_advanced_panel.html +84 -71
- nautobot/core/templates/inc/page_title.html +23 -0
- nautobot/core/templates/inc/paginator.html +39 -28
- nautobot/core/templates/inc/relationships/panel_override.html +3 -3
- nautobot/core/templates/inc/relationships_panel.html +3 -3
- nautobot/core/templates/inc/relationships_table_rows.html +1 -1
- nautobot/core/templates/inc/search_panel.html +22 -16
- nautobot/core/templates/inc/table.html +61 -36
- nautobot/core/templates/inc/tenancy_form_panel.html +3 -3
- nautobot/core/templates/login.html +17 -59
- nautobot/core/templates/modals/modal_theme.html +12 -23
- nautobot/core/templates/nautobot_config.py.j2 +6 -5
- nautobot/core/templates/panel_table.html +8 -12
- nautobot/core/templates/redoc_ui.html +80 -0
- nautobot/core/templates/rest_framework/api.html +43 -21
- nautobot/core/templates/search.html +12 -13
- nautobot/core/templates/swagger_ui.html +19 -4
- nautobot/core/templates/system_jobs/import_objects.html +70 -58
- nautobot/core/templates/template.css +0 -6
- nautobot/core/templates/utilities/comment_form.html +34 -0
- nautobot/core/templates/utilities/confirmation_form.html +17 -9
- nautobot/core/templates/utilities/obj_table.html +19 -11
- nautobot/core/templates/utilities/render_field.html +27 -21
- nautobot/core/templates/utilities/render_jinja2.html +22 -25
- nautobot/core/templates/utilities/templatetags/advanced_filter_indicator.html +8 -0
- nautobot/core/templates/utilities/templatetags/badge.html +1 -1
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +2 -3
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +482 -0
- nautobot/core/templates/utilities/templatetags/modal_form_as_dialog.html +14 -18
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +11 -11
- nautobot/core/templates/utilities/templatetags/table_config_form.html +51 -24
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/templatetags/utilization_graph.html +3 -3
- nautobot/core/templates/utilities/theme_preview.html +829 -566
- nautobot/core/templates/utilities/worker_status.html +42 -41
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templates/widgets/sluginput.html +2 -2
- nautobot/core/templatetags/buttons.py +38 -40
- nautobot/core/templatetags/helpers.py +105 -28
- nautobot/core/templatetags/ui_framework.py +17 -0
- nautobot/core/testing/api.py +76 -12
- nautobot/core/testing/filters.py +11 -27
- nautobot/core/testing/integration.py +128 -10
- nautobot/core/testing/mixins.py +7 -4
- nautobot/core/testing/utils.py +28 -5
- nautobot/core/testing/views.py +125 -27
- nautobot/core/tests/integration/test_app_home.py +39 -35
- nautobot/core/tests/integration/test_app_navbar.py +60 -67
- nautobot/core/tests/integration/test_filters.py +123 -55
- nautobot/core/tests/integration/test_general_functionality.py +1 -1
- nautobot/core/tests/integration/test_home.py +10 -18
- nautobot/core/tests/integration/test_import_objects_ui.py +2 -9
- nautobot/core/tests/integration/test_navbar.py +41 -16
- nautobot/core/tests/integration/test_swagger.py +1 -7
- nautobot/core/tests/integration/test_theme.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +6 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_branching.py +154 -0
- nautobot/core/tests/test_breadcrumbs.py +7 -8
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_commands.py +0 -41
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_csv.py +4 -7
- nautobot/core/tests/test_filters.py +326 -318
- nautobot/core/tests/test_forms.py +19 -30
- nautobot/core/tests/test_graphql.py +67 -57
- nautobot/core/tests/test_models.py +1 -1
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +78 -10
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +61 -21
- nautobot/core/tests/test_templatetags_ui_framework.py +36 -18
- nautobot/core/tests/test_ui.py +207 -2
- nautobot/core/tests/test_utils.py +147 -2
- nautobot/core/tests/test_views.py +201 -64
- nautobot/core/tests/test_views_utils.py +1 -1
- nautobot/core/ui/breadcrumbs.py +2 -12
- nautobot/core/ui/choices.py +190 -0
- nautobot/core/ui/constants.py +86 -0
- nautobot/core/ui/echarts.py +474 -0
- nautobot/core/ui/nav.py +5 -1
- nautobot/core/ui/object_detail.py +180 -16
- nautobot/core/urls.py +13 -1
- nautobot/core/utils/cache.py +71 -0
- nautobot/core/utils/data.py +8 -5
- nautobot/core/utils/filtering.py +8 -2
- nautobot/core/utils/git.py +3 -3
- nautobot/core/utils/lookup.py +87 -13
- nautobot/core/utils/migrations.py +22 -0
- nautobot/core/utils/module_loading.py +26 -0
- nautobot/core/utils/permissions.py +9 -5
- nautobot/core/views/__init__.py +114 -63
- nautobot/core/views/generic.py +34 -27
- nautobot/core/views/mixins.py +49 -27
- nautobot/core/views/renderers.py +3 -5
- nautobot/core/views/utils.py +10 -5
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/__init__.py +0 -0
- nautobot/data_validation/api/__init__.py +1 -0
- nautobot/data_validation/api/serializers.py +80 -0
- nautobot/data_validation/api/urls.py +20 -0
- nautobot/data_validation/api/views.py +44 -0
- nautobot/data_validation/apps.py +18 -0
- nautobot/data_validation/custom_validators.py +330 -0
- nautobot/data_validation/filters.py +133 -0
- nautobot/data_validation/form_mixin.py +25 -0
- nautobot/data_validation/forms.py +342 -0
- nautobot/data_validation/migrations/0001_initial.py +224 -0
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +324 -0
- nautobot/data_validation/migrations/__init__.py +0 -0
- nautobot/data_validation/models.py +361 -0
- nautobot/data_validation/navigation.py +74 -0
- nautobot/data_validation/signals.py +30 -0
- nautobot/data_validation/tables.py +259 -0
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +1 -0
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +11 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +20 -0
- nautobot/data_validation/tests/migrations/__init__.py +0 -0
- nautobot/data_validation/tests/migrations/test_migrations.py +489 -0
- nautobot/data_validation/tests/test_api.py +238 -0
- nautobot/data_validation/tests/test_custom_validators.py +423 -0
- nautobot/data_validation/tests/test_data_compliance_rules.py +85 -0
- nautobot/data_validation/tests/test_filters.py +240 -0
- nautobot/data_validation/tests/test_form_mixin.py +115 -0
- nautobot/data_validation/tests/test_models.py +393 -0
- nautobot/data_validation/tests/test_views.py +435 -0
- nautobot/data_validation/urls.py +21 -0
- nautobot/data_validation/views.py +227 -0
- nautobot/dcim/api/serializers.py +10 -13
- nautobot/dcim/api/urls.py +2 -0
- nautobot/dcim/api/views.py +7 -0
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +16 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +70 -157
- nautobot/dcim/forms.py +12 -6
- nautobot/dcim/graphql/types.py +1 -0
- nautobot/dcim/migrations/0075_add_deviceclusterassignment.py +52 -0
- nautobot/dcim/migrations/0076_device_cluster_to_clusters_data_migration.py +40 -0
- nautobot/dcim/migrations/0077_remove_device_cluster.py +14 -0
- nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
- nautobot/dcim/models/__init__.py +2 -0
- nautobot/dcim/models/device_components.py +3 -1
- nautobot/dcim/models/devices.py +115 -51
- nautobot/dcim/navigation.py +7 -3
- nautobot/dcim/querysets.py +6 -0
- nautobot/dcim/signals.py +19 -0
- nautobot/dcim/tables/devices.py +9 -5
- nautobot/dcim/tables/template_code.py +191 -102
- nautobot/dcim/templates/dcim/cable.html +1 -1
- nautobot/dcim/templates/dcim/cable_connect.html +62 -146
- nautobot/dcim/templates/dcim/cable_retrieve.html +10 -10
- nautobot/dcim/templates/dcim/cable_trace.html +15 -17
- nautobot/dcim/templates/dcim/console_port_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/consoleport.html +18 -17
- nautobot/dcim/templates/dcim/consoleserverport.html +18 -17
- nautobot/dcim/templates/dcim/controller_create.html +12 -8
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +1 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +6 -6
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/device/config.html +17 -19
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +4 -4
- nautobot/dcim/templates/dcim/device/status.html +20 -20
- nautobot/dcim/templates/dcim/device_component_add.html +24 -15
- nautobot/dcim/templates/dcim/device_create.html +120 -120
- nautobot/dcim/templates/dcim/device_list.html +75 -12
- nautobot/dcim/templates/dcim/devicebay.html +7 -7
- nautobot/dcim/templates/dcim/devicebay_populate.html +29 -23
- nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +6 -6
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/devicetype_component_add.html +25 -19
- nautobot/dcim/templates/dcim/devicetype_list.html +4 -4
- nautobot/dcim/templates/dcim/devicetype_update.html +9 -9
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +3 -3
- nautobot/dcim/templates/dcim/frontport.html +21 -20
- nautobot/dcim/templates/dcim/inc/cable_form.html +7 -7
- nautobot/dcim/templates/dcim/inc/cable_termination.html +1 -1
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +18 -9
- nautobot/dcim/templates/dcim/inc/detail_softwareversion_softwareimagefile_rows.html +1 -1
- nautobot/dcim/templates/dcim/inc/device_interface_filter.html +1 -1
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/homepage_connections.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +42 -22
- nautobot/dcim/templates/dcim/interface_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/interface_edit.html +26 -11
- nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem_add.html +21 -10
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +6 -4
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +24 -18
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/location_update.html +9 -9
- nautobot/dcim/templates/dcim/locationtype.html +0 -1
- nautobot/dcim/templates/dcim/module/base.html +67 -27
- nautobot/dcim/templates/dcim/module_consoleports.html +13 -15
- nautobot/dcim/templates/dcim/module_consoleserverports.html +13 -15
- nautobot/dcim/templates/dcim/module_frontports.html +13 -15
- nautobot/dcim/templates/dcim/module_interfaces.html +14 -16
- nautobot/dcim/templates/dcim/module_list.html +59 -10
- nautobot/dcim/templates/dcim/module_modulebays.html +12 -14
- nautobot/dcim/templates/dcim/module_poweroutlets.html +13 -15
- nautobot/dcim/templates/dcim/module_powerports.html +13 -15
- nautobot/dcim/templates/dcim/module_rearports.html +13 -15
- nautobot/dcim/templates/dcim/module_retrieve.html +3 -3
- nautobot/dcim/templates/dcim/module_update.html +15 -9
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -93
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +7 -7
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +74 -35
- nautobot/dcim/templates/dcim/platform_create.html +9 -9
- nautobot/dcim/templates/dcim/power_port_connection_list.html +3 -3
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed_edit.html +15 -15
- nautobot/dcim/templates/dcim/poweroutlet.html +13 -13
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +17 -16
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/rack_elevation_list.html +21 -9
- nautobot/dcim/templates/dcim/rack_retrieve.html +75 -57
- nautobot/dcim/templates/dcim/rack_update.html +14 -14
- nautobot/dcim/templates/dcim/rackreservation.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation_edit.html +6 -6
- nautobot/dcim/templates/dcim/rearport.html +19 -18
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/trace/circuit.html +1 -1
- nautobot/dcim/templates/dcim/trace/device.html +1 -1
- nautobot/dcim/templates/dcim/trace/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/trace/termination.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +25 -16
- nautobot/dcim/templates/dcim/virtualchassis_create.html +6 -6
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_update.html +36 -22
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +9 -9
- nautobot/dcim/tests/integration/test_controller.py +6 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +7 -7
- nautobot/dcim/tests/integration/test_create_device.py +9 -9
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +7 -2
- nautobot/dcim/tests/integration/test_fileinputpicker.py +5 -7
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +2 -0
- nautobot/dcim/tests/integration/test_module_bay_position.py +4 -1
- nautobot/dcim/tests/test_api.py +86 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +159 -110
- nautobot/dcim/tests/test_graphql.py +32 -36
- nautobot/dcim/tests/test_jobs.py +1 -1
- nautobot/dcim/tests/test_models.py +229 -1
- nautobot/dcim/tests/test_views.py +31 -20
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +77 -41
- nautobot/extras/api/serializers.py +83 -19
- nautobot/extras/api/urls.py +7 -0
- nautobot/extras/api/views.py +243 -140
- nautobot/extras/choices.py +34 -13
- nautobot/extras/constants.py +1 -1
- nautobot/extras/context_managers.py +26 -26
- nautobot/extras/datasources/git.py +22 -0
- nautobot/extras/datasources/registry.py +3 -0
- nautobot/extras/exceptions.py +5 -0
- nautobot/extras/factory.py +11 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +4 -3
- nautobot/extras/{filters/__init__.py → filters.py} +203 -58
- nautobot/extras/forms/base.py +2 -1
- nautobot/extras/forms/forms.py +225 -20
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/homepage.py +21 -2
- nautobot/extras/jobs.py +2 -8
- nautobot/extras/jobs_ui.py +2 -2
- nautobot/extras/management/__init__.py +9 -0
- nautobot/extras/managers.py +31 -22
- nautobot/extras/migrations/0126_approval_workflow_pre_check.py +58 -0
- nautobot/extras/migrations/0127_approval_workflow_models.py +266 -0
- nautobot/extras/migrations/0128_remove_job_approval_required_and_more.py +29 -0
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/models/__init__.py +14 -3
- nautobot/extras/models/approvals.py +556 -0
- nautobot/extras/models/change_logging.py +1 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/customfields.py +57 -22
- nautobot/extras/models/datasources.py +21 -0
- nautobot/extras/models/groups.py +2 -0
- nautobot/extras/models/jobs.py +122 -39
- nautobot/extras/models/metadata.py +2 -3
- nautobot/extras/models/mixins.py +129 -1
- nautobot/extras/models/models.py +22 -14
- nautobot/extras/models/relationships.py +47 -10
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/models/tags.py +1 -1
- nautobot/extras/navigation.py +42 -15
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +1 -23
- nautobot/extras/plugins/tables.py +8 -6
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/validators.py +10 -10
- nautobot/extras/plugins/views.py +1 -5
- nautobot/extras/querysets.py +17 -21
- nautobot/extras/signals.py +23 -8
- nautobot/extras/tables.py +420 -99
- nautobot/extras/templates/extras/approval_dashboard.html +15 -0
- nautobot/extras/templates/extras/approval_workflow/approve.html +11 -0
- nautobot/extras/templates/extras/approval_workflow/comment.html +9 -0
- nautobot/extras/templates/extras/approval_workflow/deny.html +10 -0
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +77 -0
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +29 -0
- nautobot/extras/templates/extras/configcontext_update.html +12 -12
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +9 -9
- nautobot/extras/templates/extras/configcontextschema_update.html +6 -6
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/customfield_update.html +12 -12
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_edit.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +17 -17
- nautobot/extras/templates/extras/dynamicgroup_update.html +24 -24
- nautobot/extras/templates/extras/externalintegration_update.html +6 -6
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/gitrepository_object_edit.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +1 -1
- nautobot/extras/templates/extras/gitrepository_retrieve.html +12 -12
- nautobot/extras/templates/extras/gitrepository_update.html +25 -7
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -1
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +38 -0
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +14 -13
- nautobot/extras/templates/extras/inc/configcontext_format.html +11 -4
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +7 -7
- nautobot/extras/templates/extras/inc/job_label.html +5 -5
- nautobot/extras/templates/extras/inc/job_table.html +23 -10
- nautobot/extras/templates/extras/inc/job_tiles.html +33 -21
- nautobot/extras/templates/extras/inc/jobresult.html +6 -6
- nautobot/extras/templates/extras/inc/json_format.html +11 -4
- nautobot/extras/templates/extras/inc/object_contact_header.html +6 -6
- nautobot/extras/templates/extras/inc/overridable_field.html +16 -15
- nautobot/extras/templates/extras/inc/panel_approvalworkflowstage.html +34 -0
- nautobot/extras/templates/extras/inc/panel_changelog.html +9 -9
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +8 -6
- nautobot/extras/templates/extras/inc/tags_panel.html +3 -3
- nautobot/extras/templates/extras/job.html +154 -155
- nautobot/extras/templates/extras/job_approval_confirmation.html +4 -27
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -1
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/job_edit.html +69 -64
- nautobot/extras/templates/extras/job_list.html +37 -60
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/jobresult_retrieve.html +17 -17
- nautobot/extras/templates/extras/marketplace.html +62 -71
- nautobot/extras/templates/extras/metadatatype_create.html +9 -9
- nautobot/extras/templates/extras/note.html +1 -1
- nautobot/extras/templates/extras/object_approvalworkflow.html +36 -0
- nautobot/extras/templates/extras/object_assign_contact_or_team.html +16 -7
- nautobot/extras/templates/extras/object_configcontext.html +20 -20
- nautobot/extras/templates/extras/object_new_contact.html +6 -6
- nautobot/extras/templates/extras/object_new_team.html +6 -6
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/objectchange_retrieve.html +37 -56
- nautobot/extras/templates/extras/plugin_detail.html +40 -41
- nautobot/extras/templates/extras/plugins_list.html +23 -38
- nautobot/extras/templates/extras/plugins_tiles.html +28 -28
- nautobot/extras/templates/extras/role_retrieve.html +112 -48
- nautobot/extras/templates/extras/scheduledjob.html +25 -28
- nautobot/extras/templates/extras/secret_create.html +11 -11
- nautobot/extras/templates/extras/secretsgroup_update.html +6 -6
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +3 -3
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/tag_update.html +3 -3
- nautobot/extras/templates/extras/templatetags/log_level.html +1 -1
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -2
- nautobot/extras/templates/extras/webhook.html +12 -12
- nautobot/extras/templatetags/approvals.py +19 -0
- 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 +5 -8
- nautobot/extras/tests/integration/test_configcontextschema.py +43 -48
- nautobot/extras/tests/integration/test_customfields.py +33 -33
- nautobot/extras/tests/integration/test_dynamicgroups.py +5 -10
- nautobot/extras/tests/integration/test_jobs.py +2 -4
- nautobot/extras/tests/integration/test_notes.py +3 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +35 -27
- nautobot/extras/tests/integration/test_relationships.py +7 -11
- nautobot/extras/tests/integration/test_tagfilter.py +3 -11
- nautobot/extras/tests/test_api.py +786 -242
- nautobot/extras/tests/test_approvals.py +715 -0
- nautobot/extras/tests/test_changelog.py +18 -14
- nautobot/extras/tests/test_customfields.py +14 -13
- nautobot/extras/tests/test_datasources.py +1 -1
- nautobot/extras/tests/test_dynamicgroups.py +9 -4
- nautobot/extras/tests/test_filters.py +443 -13
- nautobot/extras/tests/test_forms.py +18 -57
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +81 -1
- nautobot/extras/tests/test_models.py +378 -47
- nautobot/extras/tests/test_plugins.py +47 -13
- nautobot/extras/tests/test_relationships.py +7 -2
- nautobot/extras/tests/test_utils.py +2 -0
- nautobot/extras/tests/test_views.py +780 -493
- nautobot/extras/urls.py +36 -12
- nautobot/extras/utils.py +58 -12
- nautobot/extras/views.py +668 -209
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +35 -71
- 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 -1
- nautobot/ipam/querysets.py +1 -2
- nautobot/ipam/tables.py +26 -17
- nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +6 -6
- nautobot/ipam/templates/ipam/inc/service.html +8 -8
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +3 -2
- nautobot/ipam/templates/ipam/ipaddress.html +27 -13
- nautobot/ipam/templates/ipam/ipaddress_assign.html +31 -24
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +3 -3
- nautobot/ipam/templates/ipam/ipaddress_edit.html +9 -9
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddress_merge.html +195 -186
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +7 -5
- 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_create.html +9 -9
- nautobot/ipam/templates/ipam/prefix_list.html +15 -14
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_update.html +6 -6
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +15 -15
- nautobot/ipam/tests/integration/test_prefixes.py +5 -13
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +20 -7
- nautobot/ipam/tests/test_filters.py +10 -0
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +24 -21
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +49 -7
- nautobot/project-static/dist/css/graphql-libraries.css +655 -0
- nautobot/project-static/dist/css/graphql-libraries.css.map +1 -0
- nautobot/project-static/dist/css/materialdesignicons.css +3 -0
- nautobot/project-static/dist/css/materialdesignicons.css.map +1 -0
- nautobot/project-static/dist/css/nautobot.css +13 -0
- nautobot/project-static/dist/css/nautobot.css.map +1 -0
- nautobot/project-static/dist/js/graphql-libraries.js +3 -0
- nautobot/project-static/dist/js/graphql-libraries.js.LICENSE.txt +62 -0
- nautobot/project-static/dist/js/graphql-libraries.js.map +1 -0
- nautobot/project-static/dist/js/libraries.js +3 -0
- nautobot/project-static/dist/js/libraries.js.LICENSE.txt +65 -0
- nautobot/project-static/dist/js/libraries.js.map +1 -0
- nautobot/project-static/dist/js/materialdesignicons.js +0 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js +2 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -0
- nautobot/project-static/dist/js/nautobot.js +2 -0
- nautobot/project-static/dist/js/nautobot.js.map +1 -0
- nautobot/project-static/fonts/Montserrat-v30-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Light.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Regular.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Light.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Regular.woff2 +0 -0
- nautobot/project-static/img/jinja_logo.svg +21 -92
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/editor.js +4 -4
- nautobot/project-static/js/forms.js +67 -717
- nautobot/project-static/js/job_result.js +2 -2
- nautobot/project-static/nautobot-icons/360-degrees.svg +3 -0
- nautobot/project-static/nautobot-icons/arrow-decision.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-expand-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-2-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/atom.svg +3 -0
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/branch.svg +3 -0
- nautobot/project-static/nautobot-icons/briefcase-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data.svg +3 -0
- nautobot/project-static/nautobot-icons/cast.svg +3 -0
- nautobot/project-static/nautobot-icons/check-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-check.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-upload.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +3 -0
- nautobot/project-static/nautobot-icons/compass.svg +3 -0
- nautobot/project-static/nautobot-icons/control-panel.svg +3 -0
- nautobot/project-static/nautobot-icons/credit-card.svg +3 -0
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +3 -0
- nautobot/project-static/nautobot-icons/direction.svg +3 -0
- nautobot/project-static/nautobot-icons/elements.svg +3 -0
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/globe-2.svg +3 -0
- nautobot/project-static/nautobot-icons/globe.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +3 -0
- nautobot/project-static/nautobot-icons/history.svg +3 -0
- nautobot/project-static/nautobot-icons/ip.svg +3 -0
- nautobot/project-static/nautobot-icons/laptop.svg +3 -0
- nautobot/project-static/nautobot-icons/lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/list-unordered.svg +3 -0
- nautobot/project-static/nautobot-icons/map-view.svg +3 -0
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-2.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-3.svg +3 -0
- nautobot/project-static/nautobot-icons/plug.svg +3 -0
- nautobot/project-static/nautobot-icons/refresh-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/rocket-2.svg +3 -0
- nautobot/project-static/nautobot-icons/rotate-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/route.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +3 -0
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server-2.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +3 -0
- nautobot/project-static/nautobot-icons/share.svg +3 -0
- nautobot/project-static/nautobot-icons/shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/sitemap-outline.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert-2.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert.svg +3 -0
- nautobot/project-static/nautobot-icons/star-filled.svg +3 -0
- nautobot/project-static/nautobot-icons/star.svg +3 -0
- nautobot/project-static/nautobot-icons/transform.svg +3 -0
- nautobot/project-static/nautobot-icons/wifi.svg +3 -0
- 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/templates/tenancy/tenant_create.html +6 -6
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/.gitignore +137 -0
- nautobot/ui/.node-version +1 -0
- nautobot/ui/.prettierignore +3 -0
- nautobot/ui/eslint.config.js +33 -0
- nautobot/ui/package-lock.json +6594 -0
- nautobot/ui/package.json +67 -0
- nautobot/ui/prettier.config.js +9 -0
- nautobot/ui/src/js/collapse.js +69 -0
- nautobot/ui/src/js/cookie.js +31 -0
- nautobot/ui/src/js/draggable.js +101 -0
- nautobot/ui/src/js/drawer.js +106 -0
- nautobot/ui/src/js/form.js +23 -0
- nautobot/ui/src/js/history.js +51 -0
- nautobot/ui/src/js/nautobot-graphiql.js +19 -0
- nautobot/ui/src/js/nautobot.js +128 -0
- nautobot/ui/src/js/search.js +274 -0
- nautobot/ui/src/js/select2.js +318 -0
- nautobot/ui/src/js/sidenav.js +87 -0
- nautobot/ui/src/js/tabs.js +139 -0
- nautobot/ui/src/js/theme.js +104 -0
- nautobot/ui/src/js/utils.js +54 -0
- nautobot/ui/src/scss/colors.scss +58 -0
- nautobot/ui/src/scss/nautobot.scss +2471 -0
- nautobot/ui/webpack.config.js +148 -0
- nautobot/users/apps.py +3 -0
- nautobot/users/filters.py +7 -11
- nautobot/users/forms.py +10 -0
- nautobot/users/models.py +8 -0
- nautobot/users/templates/users/advanced_settings_edit.html +31 -21
- nautobot/users/templates/users/api_tokens.html +61 -51
- nautobot/users/templates/users/base.html +23 -31
- nautobot/users/templates/users/change_password.html +29 -19
- nautobot/users/templates/users/preferences.html +55 -45
- nautobot/users/templates/users/profile.html +45 -14
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/urls.py +2 -0
- nautobot/users/views.py +70 -2
- nautobot/virtualization/api/views.py +1 -1
- nautobot/virtualization/filters.py +18 -32
- nautobot/virtualization/forms.py +22 -59
- nautobot/virtualization/models.py +1 -19
- nautobot/virtualization/navigation.py +3 -1
- nautobot/virtualization/tables.py +10 -6
- nautobot/virtualization/templates/virtualization/cluster.html +13 -13
- nautobot/virtualization/templates/virtualization/cluster_edit.html +6 -6
- nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +24 -16
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +4 -4
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +27 -25
- nautobot/virtualization/templates/virtualization/vminterface.html +5 -5
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +27 -11
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_models.py +20 -5
- nautobot/virtualization/tests/test_views.py +3 -5
- nautobot/virtualization/urls.py +0 -11
- nautobot/virtualization/views.py +5 -122
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +209 -0
- nautobot/vpn/filters.py +233 -0
- nautobot/vpn/forms.py +486 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +527 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +380 -0
- nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +341 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +294 -0
- nautobot/vpn/tests/test_models.py +97 -0
- nautobot/vpn/tests/test_views.py +281 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +437 -0
- nautobot/wireless/filters.py +0 -8
- nautobot/wireless/navigation.py +3 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +6 -6
- nautobot/wireless/tests/integration/test_radio_profile.py +3 -7
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +5 -4
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +802 -707
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
- nautobot/core/management/commands/check_job_approval_status.py +0 -47
- nautobot/core/templates/search_form.html +0 -9
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +0 -87
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/extras/templates/extras/job_approval_request.html +0 -134
- nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +0 -28
- nautobot/ipam/mixins.py +0 -32
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +0 -587
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +0 -6865
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +0 -288
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.js +0 -2580
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.min.js +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/js/npm.js +0 -13
- nautobot/project-static/clipboard.js-2.0.9/clipboard.min.js +0 -7
- nautobot/project-static/css/base.css +0 -1040
- nautobot/project-static/css/dark.css +0 -282
- nautobot/project-static/flatpickr-4.6.9/flatpickr.min.js +0 -2
- nautobot/project-static/flatpickr-4.6.9/themes/light.min.css +0 -1
- nautobot/project-static/graphiql-1.5.16/graphiql.min.css +0 -12
- nautobot/project-static/graphiql-1.5.16/graphiql.min.js +0 -11
- nautobot/project-static/highlight.js-11.9.0/github-dark.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/github.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/highlight.min.js +0 -378
- nautobot/project-static/jquery/jquery-3.7.1.min.js +0 -2
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_444444_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_555555_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777620_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777777_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_cc0000_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_ffffff_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +0 -7
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +0 -6
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +0 -5
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.theme.min.css +0 -5
- nautobot/project-static/js/homepage_layout.js +0 -182
- nautobot/project-static/js/nav_menu.js +0 -250
- nautobot/project-static/js/theme.js +0 -133
- nautobot/project-static/materialdesignicons-7.4.47/LICENSE +0 -20
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/react-16.14.0/react.production.min.js +0 -32
- nautobot/project-static/react-dom-16.14.0/react-dom.production.min.js +0 -239
- nautobot/project-static/select2-4.0.13/i18n/af.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ar.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/az.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bg.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bn.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ca.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/cs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/da.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/de.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/dsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/el.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/en.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/es.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/et.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/eu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fa.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/gl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/he.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hy.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/id.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/is.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/it.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ja.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ka.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/km.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ko.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/mk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ms.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ne.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ps.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt-BR.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ro.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ru.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sq.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr-Cyrl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/th.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/uk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/vi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-CN.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-TW.js +0 -3
- nautobot/project-static/select2-4.0.13/select2.min.css +0 -1
- nautobot/project-static/select2-4.0.13/select2.min.js +0 -2
- nautobot/project-static/select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css +0 -7
- nautobot/project-static/subscriptions-transport-ws-0.9.18/client.min.js +0 -8
- nautobot/project-static/whatwg-fetch-3.6.2/fetch.umd.min.js +0 -8
- nautobot/virtualization/templates/virtualization/cluster_add_devices.html +0 -37
- /nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.ttf → dist/1fcc36272ea3e53d0031.ttf} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.eot → dist/2146c3c82b553977abc7.eot} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff → dist/e55a20c80650829ec5fd.woff} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 → dist/ec024da790d2972da002.woff2} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
nautobot/extras/forms/forms.py
CHANGED
|
@@ -5,6 +5,7 @@ from celery import chain
|
|
|
5
5
|
from django import forms
|
|
6
6
|
from django.conf import settings
|
|
7
7
|
from django.contrib.auth import get_user_model
|
|
8
|
+
from django.contrib.auth.models import Group
|
|
8
9
|
from django.contrib.contenttypes.models import ContentType
|
|
9
10
|
from django.core.exceptions import ValidationError
|
|
10
11
|
from django.core.validators import MinValueValidator
|
|
@@ -25,6 +26,7 @@ from nautobot.core.forms import (
|
|
|
25
26
|
CommentField,
|
|
26
27
|
CSVContentTypeField,
|
|
27
28
|
CSVModelForm,
|
|
29
|
+
DatePicker,
|
|
28
30
|
DateTimePicker,
|
|
29
31
|
DynamicModelChoiceField,
|
|
30
32
|
DynamicModelMultipleChoiceField,
|
|
@@ -37,11 +39,12 @@ from nautobot.core.forms import (
|
|
|
37
39
|
TagFilterField,
|
|
38
40
|
)
|
|
39
41
|
from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
|
|
42
|
+
from nautobot.core.forms.fields import MultiValueCharField
|
|
40
43
|
from nautobot.core.forms.forms import ConfirmationForm
|
|
41
44
|
from nautobot.core.forms.widgets import ClearableFileInput
|
|
42
|
-
from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
|
|
43
45
|
from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
44
46
|
from nautobot.extras.choices import (
|
|
47
|
+
ApprovalWorkflowStateChoices,
|
|
45
48
|
ButtonClassChoices,
|
|
46
49
|
CustomFieldFilterLogicChoices,
|
|
47
50
|
DynamicGroupTypeChoices,
|
|
@@ -55,6 +58,11 @@ from nautobot.extras.choices import (
|
|
|
55
58
|
from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
|
|
56
59
|
from nautobot.extras.datasources import get_datasource_content_choices
|
|
57
60
|
from nautobot.extras.models import (
|
|
61
|
+
ApprovalWorkflow,
|
|
62
|
+
ApprovalWorkflowDefinition,
|
|
63
|
+
ApprovalWorkflowStage,
|
|
64
|
+
ApprovalWorkflowStageDefinition,
|
|
65
|
+
ApprovalWorkflowStageResponse,
|
|
58
66
|
ComputedField,
|
|
59
67
|
ConfigContext,
|
|
60
68
|
ConfigContextSchema,
|
|
@@ -117,12 +125,23 @@ from .mixins import (
|
|
|
117
125
|
CustomFieldModelFormMixin,
|
|
118
126
|
NoteModelBulkEditFormMixin,
|
|
119
127
|
NoteModelFormMixin,
|
|
128
|
+
RelationshipModelFormMixin,
|
|
120
129
|
TagsBulkEditFormMixin,
|
|
121
130
|
)
|
|
122
131
|
|
|
123
132
|
logger = logging.getLogger(__name__)
|
|
124
133
|
|
|
125
134
|
__all__ = (
|
|
135
|
+
"ApprovalWorkflowDefinitionBulkEditForm",
|
|
136
|
+
"ApprovalWorkflowDefinitionFilterForm",
|
|
137
|
+
"ApprovalWorkflowDefinitionForm",
|
|
138
|
+
"ApprovalWorkflowFilterForm",
|
|
139
|
+
"ApprovalWorkflowStageDefinitionBulkEditForm",
|
|
140
|
+
"ApprovalWorkflowStageDefinitionFilterForm",
|
|
141
|
+
"ApprovalWorkflowStageDefinitionForm",
|
|
142
|
+
"ApprovalWorkflowStageDefinitionFormSet",
|
|
143
|
+
"ApprovalWorkflowStageFilterForm",
|
|
144
|
+
"ApprovalWorkflowStageResponseFilterForm",
|
|
126
145
|
"BaseDynamicGroupMembershipFormSet",
|
|
127
146
|
"ComputedFieldBulkEditForm",
|
|
128
147
|
"ComputedFieldFilterForm",
|
|
@@ -133,7 +152,6 @@ __all__ = (
|
|
|
133
152
|
"ConfigContextSchemaBulkEditForm",
|
|
134
153
|
"ConfigContextSchemaFilterForm",
|
|
135
154
|
"ConfigContextSchemaForm",
|
|
136
|
-
"CustomFieldBulkCreateForm", # 2.0 TODO remove this deprecated class
|
|
137
155
|
"CustomFieldBulkDeleteForm",
|
|
138
156
|
"CustomFieldBulkEditForm",
|
|
139
157
|
"CustomFieldChoiceFormSet",
|
|
@@ -215,6 +233,211 @@ __all__ = (
|
|
|
215
233
|
"WebhookForm",
|
|
216
234
|
)
|
|
217
235
|
|
|
236
|
+
#
|
|
237
|
+
# Approval Workflows
|
|
238
|
+
#
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ApprovalWorkflowDefinitionForm(
|
|
242
|
+
BootstrapMixin,
|
|
243
|
+
CustomFieldModelFormMixin,
|
|
244
|
+
NoteModelFormMixin,
|
|
245
|
+
RelationshipModelFormMixin,
|
|
246
|
+
):
|
|
247
|
+
"""Form for creating and updating ApprovalWorkflowDefinition."""
|
|
248
|
+
|
|
249
|
+
model_content_type = forms.ModelChoiceField(
|
|
250
|
+
queryset=ContentType.objects.filter(FeatureQuery("approval_workflows").get_query()).order_by(
|
|
251
|
+
"app_label", "model"
|
|
252
|
+
),
|
|
253
|
+
required=True,
|
|
254
|
+
label="Model Content Type",
|
|
255
|
+
)
|
|
256
|
+
model_constraints = JSONField(
|
|
257
|
+
required=False,
|
|
258
|
+
label="Model Constraints",
|
|
259
|
+
help_text="Constraints for filtering selected model content type.<br>"
|
|
260
|
+
"Supports simple Django field lookups.<br>"
|
|
261
|
+
'Enter in <a href="https://json.org/">JSON</a> format.',
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
class Meta:
|
|
265
|
+
"""Meta attributes."""
|
|
266
|
+
|
|
267
|
+
model = ApprovalWorkflowDefinition
|
|
268
|
+
fields = "__all__"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class ApprovalWorkflowDefinitionBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
272
|
+
"""ApprovalWorkflowDefinition bulk edit form."""
|
|
273
|
+
|
|
274
|
+
pk = forms.ModelMultipleChoiceField(
|
|
275
|
+
queryset=ApprovalWorkflowDefinition.objects.all(), widget=forms.MultipleHiddenInput
|
|
276
|
+
)
|
|
277
|
+
model_content_type = forms.ModelChoiceField(
|
|
278
|
+
queryset=ContentType.objects.filter(FeatureQuery("approval_workflows").get_query()).order_by(
|
|
279
|
+
"app_label", "model"
|
|
280
|
+
),
|
|
281
|
+
required=True,
|
|
282
|
+
label="Model Content Type",
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
class Meta:
|
|
286
|
+
"""Meta attributes."""
|
|
287
|
+
|
|
288
|
+
model = ApprovalWorkflowDefinition
|
|
289
|
+
nullable_fields = ["model_constraints"]
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class ApprovalWorkflowDefinitionFilterForm(NautobotFilterForm):
|
|
293
|
+
"""Filter form for ApprovalWorkflowDefinition."""
|
|
294
|
+
|
|
295
|
+
model = ApprovalWorkflowDefinition
|
|
296
|
+
q = forms.CharField(required=False, label="Search")
|
|
297
|
+
name = MultiValueCharField(required=False)
|
|
298
|
+
model_content_type = MultipleContentTypeField(
|
|
299
|
+
queryset=ContentType.objects.filter(FeatureQuery("approval_workflows").get_query()).order_by(
|
|
300
|
+
"app_label", "model"
|
|
301
|
+
),
|
|
302
|
+
required=False,
|
|
303
|
+
)
|
|
304
|
+
tags = TagFilterField(model)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class ApprovalWorkflowStageDefinitionForm(NautobotModelForm):
|
|
308
|
+
"""Form for creating and updating ApprovalWorkflowStageDefinition."""
|
|
309
|
+
|
|
310
|
+
approval_workflow_definition = DynamicModelChoiceField(
|
|
311
|
+
queryset=ApprovalWorkflowDefinition.objects.all(),
|
|
312
|
+
required=True,
|
|
313
|
+
label="Approval Workflow Definition",
|
|
314
|
+
)
|
|
315
|
+
approver_group = DynamicModelChoiceField(
|
|
316
|
+
queryset=Group.objects.all(),
|
|
317
|
+
required=True,
|
|
318
|
+
label="Approver Group",
|
|
319
|
+
help_text="User group that can approve this stage.",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
class Meta:
|
|
323
|
+
"""Meta attributes."""
|
|
324
|
+
|
|
325
|
+
model = ApprovalWorkflowStageDefinition
|
|
326
|
+
fields = "__all__"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# ApprovalWorkFlow inline formset for use with providing dynamic rows when creating/editing choices
|
|
330
|
+
# for `ApprovalWorkFlowInstance` objects in UI views. Fields/exclude must be set but since we're using all the
|
|
331
|
+
# fields we're just setting `exclude=()` here.
|
|
332
|
+
ApprovalWorkflowStageDefinitionFormSet = inlineformset_factory(
|
|
333
|
+
parent_model=ApprovalWorkflowDefinition,
|
|
334
|
+
model=ApprovalWorkflowStageDefinition,
|
|
335
|
+
exclude=("_custom_field_data",),
|
|
336
|
+
extra=5,
|
|
337
|
+
widgets={
|
|
338
|
+
"name": forms.TextInput(attrs={"class": "form-control"}),
|
|
339
|
+
"sequence": forms.NumberInput(attrs={"class": "form-control"}),
|
|
340
|
+
"min_approvers": forms.NumberInput(attrs={"class": "form-control"}),
|
|
341
|
+
"denial_message": forms.TextInput(attrs={"class": "form-control"}),
|
|
342
|
+
"approver_group": forms.Select(attrs={"class": "form-control"}),
|
|
343
|
+
},
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class ApprovalWorkflowStageDefinitionBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
348
|
+
"""ApprovalWorkflowStageDefinition bulk edit form."""
|
|
349
|
+
|
|
350
|
+
pk = forms.ModelMultipleChoiceField(
|
|
351
|
+
queryset=ApprovalWorkflowStageDefinition.objects.all(), widget=forms.MultipleHiddenInput
|
|
352
|
+
)
|
|
353
|
+
sequence = forms.IntegerField(required=False, label="Sequence")
|
|
354
|
+
min_approvers = forms.IntegerField(required=False, label="Minimum Approvers")
|
|
355
|
+
denial_message = forms.CharField(required=False, label="Denial Message")
|
|
356
|
+
|
|
357
|
+
class Meta:
|
|
358
|
+
"""Meta attributes."""
|
|
359
|
+
|
|
360
|
+
model = ApprovalWorkflowStageDefinition
|
|
361
|
+
nullable_fields = ["denial_message"]
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class ApprovalWorkflowStageDefinitionFilterForm(NautobotFilterForm):
|
|
365
|
+
"""Filter form for ApprovalWorkflowStageDefinition."""
|
|
366
|
+
|
|
367
|
+
model = ApprovalWorkflowStageDefinition
|
|
368
|
+
q = forms.CharField(required=False, label="Search")
|
|
369
|
+
name = MultiValueCharField(required=False)
|
|
370
|
+
approval_workflow_definition = DynamicModelChoiceField(
|
|
371
|
+
queryset=ApprovalWorkflowDefinition.objects.all(),
|
|
372
|
+
required=False,
|
|
373
|
+
label="Approval Workflow Definition",
|
|
374
|
+
)
|
|
375
|
+
sequence = forms.IntegerField(required=False, label="Sequence")
|
|
376
|
+
min_approvers = forms.IntegerField(required=False, label="Minimum Approvers")
|
|
377
|
+
approver_group = DynamicModelChoiceField(
|
|
378
|
+
queryset=Group.objects.all(),
|
|
379
|
+
required=False,
|
|
380
|
+
label="Approver Group",
|
|
381
|
+
help_text="User group that can approve this stage.",
|
|
382
|
+
)
|
|
383
|
+
tags = TagFilterField(model)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class ApprovalWorkflowFilterForm(NautobotFilterForm):
|
|
387
|
+
"""Filter form for ApprovalWorkflow."""
|
|
388
|
+
|
|
389
|
+
model = ApprovalWorkflow
|
|
390
|
+
q = forms.CharField(required=False, label="Search")
|
|
391
|
+
approval_workflow_definition = DynamicModelChoiceField(
|
|
392
|
+
queryset=ApprovalWorkflowDefinition.objects.all(),
|
|
393
|
+
required=False,
|
|
394
|
+
label="Approval Workflow Definition",
|
|
395
|
+
)
|
|
396
|
+
object_under_review_content_type = forms.ModelChoiceField(
|
|
397
|
+
queryset=ContentType.objects.filter(FeatureQuery("approval_workflows").get_query()).order_by(
|
|
398
|
+
"app_label", "model"
|
|
399
|
+
),
|
|
400
|
+
required=False,
|
|
401
|
+
label="Object Under Review Content Type",
|
|
402
|
+
)
|
|
403
|
+
current_state = forms.ChoiceField(
|
|
404
|
+
required=False,
|
|
405
|
+
choices=add_blank_choice(ApprovalWorkflowStateChoices),
|
|
406
|
+
widget=StaticSelect2,
|
|
407
|
+
label="Current State",
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class ApprovalWorkflowStageFilterForm(NautobotFilterForm):
|
|
412
|
+
"""Filter form for ApprovalWorkflowStage."""
|
|
413
|
+
|
|
414
|
+
model = ApprovalWorkflowStage
|
|
415
|
+
q = forms.CharField(required=False, label="Search")
|
|
416
|
+
approval_workflow = DynamicModelChoiceField(
|
|
417
|
+
queryset=ApprovalWorkflow.objects.all(),
|
|
418
|
+
required=False,
|
|
419
|
+
label="Approval Workflow",
|
|
420
|
+
)
|
|
421
|
+
approval_workflow_stage_definition = DynamicModelChoiceField(
|
|
422
|
+
queryset=ApprovalWorkflowStageDefinition.objects.all(),
|
|
423
|
+
required=False,
|
|
424
|
+
label="Approval Workflow Stage Definition",
|
|
425
|
+
)
|
|
426
|
+
state = forms.ChoiceField(
|
|
427
|
+
required=False,
|
|
428
|
+
choices=add_blank_choice(ApprovalWorkflowStateChoices),
|
|
429
|
+
widget=StaticSelect2,
|
|
430
|
+
label="State",
|
|
431
|
+
)
|
|
432
|
+
decision_date = forms.DateField(widget=DatePicker(), required=False, label="Decision Date")
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class ApprovalWorkflowStageResponseFilterForm(NautobotFilterForm):
|
|
436
|
+
"""Filter form for ApprovalWorkflowStageResponse."""
|
|
437
|
+
|
|
438
|
+
model = ApprovalWorkflowStageResponse
|
|
439
|
+
q = forms.CharField(required=False, label="Search")
|
|
440
|
+
|
|
218
441
|
|
|
219
442
|
#
|
|
220
443
|
# Computed Fields
|
|
@@ -602,12 +825,6 @@ class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelFormMixin):
|
|
|
602
825
|
self.custom_fields.append(field_name)
|
|
603
826
|
|
|
604
827
|
|
|
605
|
-
# 2.0 TODO: remove this class
|
|
606
|
-
@class_deprecated_in_favor_of(CustomFieldModelBulkEditFormMixin)
|
|
607
|
-
class CustomFieldBulkCreateForm(CustomFieldModelBulkEditFormMixin):
|
|
608
|
-
"""No longer needed as a separate class - use CustomFieldModelBulkEditFormMixin instead."""
|
|
609
|
-
|
|
610
|
-
|
|
611
828
|
class CustomFieldBulkDeleteForm(ConfirmationForm):
|
|
612
829
|
def __init__(self, *args, delete_all=False, **kwargs):
|
|
613
830
|
super().__init__(*args, **kwargs)
|
|
@@ -1196,8 +1413,6 @@ class JobEditForm(NautobotModelForm):
|
|
|
1196
1413
|
"dryrun_default",
|
|
1197
1414
|
"hidden_override",
|
|
1198
1415
|
"hidden",
|
|
1199
|
-
"approval_required_override",
|
|
1200
|
-
"approval_required",
|
|
1201
1416
|
"soft_time_limit_override",
|
|
1202
1417
|
"soft_time_limit",
|
|
1203
1418
|
"time_limit_override",
|
|
@@ -1270,11 +1485,6 @@ class JobBulkEditForm(NautobotBulkEditForm):
|
|
|
1270
1485
|
has_sensitive_variables = forms.NullBooleanField(
|
|
1271
1486
|
required=False, widget=BulkEditNullBooleanSelect, help_text="Whether this job contains sensitive variables"
|
|
1272
1487
|
)
|
|
1273
|
-
approval_required = forms.NullBooleanField(
|
|
1274
|
-
required=False,
|
|
1275
|
-
widget=BulkEditNullBooleanSelect,
|
|
1276
|
-
help_text="Whether the job requires approval from another user before running",
|
|
1277
|
-
)
|
|
1278
1488
|
hidden = forms.NullBooleanField(
|
|
1279
1489
|
required=False,
|
|
1280
1490
|
widget=BulkEditNullBooleanSelect,
|
|
@@ -1341,10 +1551,6 @@ class JobBulkEditForm(NautobotBulkEditForm):
|
|
|
1341
1551
|
help_text="If checked, the default job queue will be reverted to the first value of task_queues defined in each Job's source code",
|
|
1342
1552
|
)
|
|
1343
1553
|
# Boolean overrides
|
|
1344
|
-
clear_approval_required_override = forms.BooleanField(
|
|
1345
|
-
required=False,
|
|
1346
|
-
help_text="If checked, the values of approval required will be reverted to the default values defined in each Job's source code",
|
|
1347
|
-
)
|
|
1348
1554
|
clear_dryrun_default_override = forms.BooleanField(
|
|
1349
1555
|
required=False,
|
|
1350
1556
|
help_text="If checked, the values of dryrun default will be reverted to the default values defined in each Job's source code",
|
|
@@ -1433,7 +1639,6 @@ class JobFilterForm(BootstrapMixin, forms.Form):
|
|
|
1433
1639
|
widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES),
|
|
1434
1640
|
)
|
|
1435
1641
|
read_only = forms.NullBooleanField(required=False, widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES))
|
|
1436
|
-
approval_required = forms.NullBooleanField(required=False, widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES))
|
|
1437
1642
|
is_job_hook_receiver = forms.NullBooleanField(
|
|
1438
1643
|
initial=False,
|
|
1439
1644
|
required=False,
|
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/homepage.py
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
from nautobot.core.apps import HomePageItem, HomePagePanel
|
|
2
|
-
from nautobot.extras.choices import JobResultStatusChoices
|
|
3
|
-
from nautobot.extras.models import DynamicGroup, GitRepository, JobResult, ObjectChange
|
|
2
|
+
from nautobot.extras.choices import ApprovalWorkflowStateChoices, JobResultStatusChoices
|
|
3
|
+
from nautobot.extras.models import ApprovalWorkflowStage, DynamicGroup, GitRepository, JobResult, ObjectChange
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_approval_workflow_stages(request):
|
|
7
|
+
"""Callback function to collect pending approval workflow stage instances for panel."""
|
|
8
|
+
user = request.user
|
|
9
|
+
group_pks = user.groups.all().values_list("pk", flat=True)
|
|
10
|
+
|
|
11
|
+
return ApprovalWorkflowStage.objects.filter(
|
|
12
|
+
approval_workflow__current_state=ApprovalWorkflowStateChoices.PENDING,
|
|
13
|
+
state=ApprovalWorkflowStateChoices.PENDING,
|
|
14
|
+
approval_workflow_stage_definition__approver_group__pk__in=group_pks,
|
|
15
|
+
)[:15]
|
|
4
16
|
|
|
5
17
|
|
|
6
18
|
def get_job_results(request):
|
|
@@ -41,6 +53,13 @@ layout = (
|
|
|
41
53
|
),
|
|
42
54
|
),
|
|
43
55
|
),
|
|
56
|
+
HomePagePanel(
|
|
57
|
+
name="Approval Workflow",
|
|
58
|
+
permissions=["extras.view_approvalworkflowstage"],
|
|
59
|
+
weight=600,
|
|
60
|
+
custom_data={"approval_workflow_stages": get_approval_workflow_stages},
|
|
61
|
+
custom_template="panel_approvalworkflowstage.html",
|
|
62
|
+
),
|
|
44
63
|
HomePagePanel(
|
|
45
64
|
name="Data Sources",
|
|
46
65
|
weight=700,
|
nautobot/extras/jobs.py
CHANGED
|
@@ -39,6 +39,7 @@ from nautobot.core.forms import (
|
|
|
39
39
|
JSONField,
|
|
40
40
|
)
|
|
41
41
|
from nautobot.core.forms.widgets import ClearableFileInput
|
|
42
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
42
43
|
from nautobot.core.utils.config import get_settings_or_config
|
|
43
44
|
from nautobot.core.utils.logging import sanitize
|
|
44
45
|
from nautobot.core.utils.lookup import get_model_from_name
|
|
@@ -108,7 +109,6 @@ class BaseJob:
|
|
|
108
109
|
|
|
109
110
|
- name (str)
|
|
110
111
|
- description (str)
|
|
111
|
-
- approval_required (bool)
|
|
112
112
|
- dryrun_default (bool)
|
|
113
113
|
- field_order (list)
|
|
114
114
|
- has_sensitive_variables (bool)
|
|
@@ -262,7 +262,7 @@ class BaseJob:
|
|
|
262
262
|
@classproperty
|
|
263
263
|
def singleton_cache_key(cls) -> str: # pylint: disable=no-self-argument
|
|
264
264
|
"""Cache key for singleton jobs."""
|
|
265
|
-
return
|
|
265
|
+
return construct_cache_key(cls, method_name="running", branch_aware=False, class_path=cls.class_path)
|
|
266
266
|
|
|
267
267
|
@final
|
|
268
268
|
@classproperty
|
|
@@ -353,11 +353,6 @@ class BaseJob:
|
|
|
353
353
|
def read_only(cls) -> bool: # pylint: disable=no-self-argument
|
|
354
354
|
return cls._get_meta_attr_and_assert_type("read_only", False, expected_type=bool)
|
|
355
355
|
|
|
356
|
-
@final
|
|
357
|
-
@classproperty
|
|
358
|
-
def approval_required(cls) -> bool: # pylint: disable=no-self-argument
|
|
359
|
-
return cls._get_meta_attr_and_assert_type("approval_required", False, expected_type=bool)
|
|
360
|
-
|
|
361
356
|
@final
|
|
362
357
|
@classproperty
|
|
363
358
|
def soft_time_limit(cls) -> int: # pylint: disable=no-self-argument
|
|
@@ -400,7 +395,6 @@ class BaseJob:
|
|
|
400
395
|
"name": cls.name,
|
|
401
396
|
"grouping": cls.grouping,
|
|
402
397
|
"description": cls.description,
|
|
403
|
-
"approval_required": cls.approval_required,
|
|
404
398
|
"hidden": cls.hidden,
|
|
405
399
|
"soft_time_limit": cls.soft_time_limit,
|
|
406
400
|
"time_limit": cls.time_limit,
|
nautobot/extras/jobs_ui.py
CHANGED
|
@@ -39,14 +39,14 @@ class JobKeyValueOverrideValueTablePanel(KeyValueTablePanel):
|
|
|
39
39
|
value (str): The content to display.
|
|
40
40
|
prefix (str): The label shown before the value (default: 'default is').
|
|
41
41
|
"""
|
|
42
|
-
return format_html('<span class="text-
|
|
42
|
+
return format_html('<span class="text-secondary">overridden; default is {}</span>', mark_safe(text)) # noqa: S308
|
|
43
43
|
|
|
44
44
|
def _render_overridden_block(self, content):
|
|
45
45
|
"""
|
|
46
46
|
Render a more complex block of HTML content (like rendered markdown or JSON)
|
|
47
47
|
in a div with a muted label indicating it's an overridden value.
|
|
48
48
|
"""
|
|
49
|
-
return format_html('<div class="text-
|
|
49
|
+
return format_html('<div class="text-secondary">overridden; default is:<br>{}</div>', content)
|
|
50
50
|
|
|
51
51
|
def render_description_default(self, default_value):
|
|
52
52
|
"""
|
|
@@ -11,6 +11,7 @@ from nautobot.dcim import choices as dcim_choices
|
|
|
11
11
|
from nautobot.extras import choices as extras_choices
|
|
12
12
|
from nautobot.ipam import choices as ipam_choices
|
|
13
13
|
from nautobot.virtualization import choices as vm_choices
|
|
14
|
+
from nautobot.vpn import choices as vpn_choices
|
|
14
15
|
|
|
15
16
|
# List of 2-tuples of (model_path, choiceset)
|
|
16
17
|
# Add new mappings here as other models are supported.
|
|
@@ -36,6 +37,7 @@ STATUS_CHOICESET_MAP = {
|
|
|
36
37
|
"ipam.VRF": ipam_choices.VRFStatusChoices,
|
|
37
38
|
"virtualization.VirtualMachine": vm_choices.VirtualMachineStatusChoices,
|
|
38
39
|
"virtualization.VMInterface": vm_choices.VMInterfaceStatusChoices,
|
|
40
|
+
"vpn.VPNTunnel": vpn_choices.VPNTunnelStatusChoices,
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
|
|
@@ -101,6 +103,7 @@ STATUS_DESCRIPTION_MAP = {
|
|
|
101
103
|
# Add new mappings here as other models are supported.
|
|
102
104
|
ROLE_CHOICESET_MAP = {
|
|
103
105
|
"extras.ContactAssociation": extras_choices.ContactAssociationRoleChoices,
|
|
106
|
+
"vpn.VPNTunnelEndpoint": vpn_choices.VPNTunnelEndpointRoleChoices,
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
# Map of role name -> default hex_color used when importing color choices in `export_roles_from_choiceset()`.
|
|
@@ -110,6 +113,9 @@ ROLE_COLOR_MAP = {
|
|
|
110
113
|
"Billing": ColorChoices.COLOR_GREEN,
|
|
111
114
|
"Support": ColorChoices.COLOR_YELLOW,
|
|
112
115
|
"On Site": ColorChoices.COLOR_BLACK,
|
|
116
|
+
"Hub": ColorChoices.COLOR_DARK_GREEN,
|
|
117
|
+
"Spoke": ColorChoices.COLOR_LIGHT_GREEN,
|
|
118
|
+
"Peer": ColorChoices.COLOR_ORANGE,
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
# Map of role name -> description used when importing role choices in `export_roles_from_choiceset()`.
|
|
@@ -118,6 +124,9 @@ ROLE_DESCRIPTION_MAP = {
|
|
|
118
124
|
"Billing": "Unit plays a billing role",
|
|
119
125
|
"Support": "Unit plays a support role",
|
|
120
126
|
"On Site": "Unit plays an on site role",
|
|
127
|
+
"Hub": "Unit plays a Hub role",
|
|
128
|
+
"Spoke": "Unit plays a Spoke role",
|
|
129
|
+
"Peer": "Unit plays a Peer role",
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
|
nautobot/extras/managers.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from celery import states
|
|
2
|
+
from django.contrib.auth import get_user_model
|
|
2
3
|
from django.utils import timezone
|
|
3
4
|
from django_celery_results.managers import TaskResultManager, transaction_retry
|
|
4
5
|
|
|
6
|
+
from nautobot.core.branching import BranchContext
|
|
5
7
|
from nautobot.core.models import BaseManager
|
|
6
8
|
from nautobot.core.models.querysets import RestrictedQuerySet
|
|
7
9
|
|
|
@@ -101,6 +103,8 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
|
|
|
101
103
|
}
|
|
102
104
|
from nautobot.extras.models.jobs import Job
|
|
103
105
|
|
|
106
|
+
User = get_user_model()
|
|
107
|
+
|
|
104
108
|
# Need to have a try/except block here
|
|
105
109
|
# because sometimes job_model_id will be None.
|
|
106
110
|
try:
|
|
@@ -114,28 +118,33 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
|
|
|
114
118
|
if "date_started" in kwargs:
|
|
115
119
|
fields["date_started"] = kwargs["date_started"]
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
fields["
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
121
|
+
with BranchContext(
|
|
122
|
+
branch_name=celery_kwargs.get("nautobot_job_branch_name", None),
|
|
123
|
+
user=User.objects.get(id=user_id) if user_id else None,
|
|
124
|
+
using=using,
|
|
125
|
+
):
|
|
126
|
+
obj, created = self.using(using).get_or_create(id=task_id, defaults=fields)
|
|
127
|
+
|
|
128
|
+
if not created:
|
|
129
|
+
# Make sure `date_done` is allowed to stay null until the task reacheas a ready state.
|
|
130
|
+
#
|
|
131
|
+
# Default behavior in `django-celery-results` has this field as a
|
|
132
|
+
# `DateField(auto_now=True)` which just automatically updates the `date_done` field on every
|
|
133
|
+
# state transition. This is different than Celery's default behavior (and the current
|
|
134
|
+
# behavior of Nautobot) to keep it null until there is a state transition to a ready state
|
|
135
|
+
# (e.g. `SUCCESS`, `REVOKED`, `FAILURE`).
|
|
136
|
+
if fields["status"] in states.READY_STATES:
|
|
137
|
+
fields["date_done"] = timezone.now()
|
|
138
|
+
|
|
139
|
+
# Always make sure the Job `name` is set.
|
|
140
|
+
if not obj.name and fields["task_name"]:
|
|
141
|
+
fields["name"] = fields["task_name"]
|
|
142
|
+
|
|
143
|
+
# Set the field values on the model instance.
|
|
144
|
+
for k, v in fields.items():
|
|
145
|
+
setattr(obj, k, v)
|
|
146
|
+
|
|
147
|
+
obj.save(using=using)
|
|
139
148
|
|
|
140
149
|
return obj
|
|
141
150
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Migration pre-check for deprecating the `approval_required` flag on Job.
|
|
3
|
+
|
|
4
|
+
This migration performs the following checks before proceeding:
|
|
5
|
+
1. Aborts the migration if any ScheduledJob still has `approval_required=True`,
|
|
6
|
+
because the task approval queue must be cleared before the flag can be removed.
|
|
7
|
+
2. Allows the migration to continue if any Job has `approval_required=True`,
|
|
8
|
+
but prints a warning to inform that this behavior is deprecated.
|
|
9
|
+
Users should define an approval workflow definition for such jobs going forward.
|
|
10
|
+
|
|
11
|
+
Refer to documentation for migration to the new approval workflow system:
|
|
12
|
+
https://docs.nautobot.com/projects/core/en/v3.0.0/user-guide/platform-functionality/approval-workflow/
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from django.db import migrations
|
|
16
|
+
|
|
17
|
+
from nautobot.extras.exceptions import ApprovalRequiredScheduledJobsError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _migrate_data(apps, *_):
|
|
21
|
+
ScheduledJob = apps.get_model("extras", "ScheduledJob")
|
|
22
|
+
approval_required_scheduled_jobs = ScheduledJob.objects.filter(approval_required=True).values_list("id", "name")
|
|
23
|
+
|
|
24
|
+
if approval_required_scheduled_jobs:
|
|
25
|
+
message_lines = [
|
|
26
|
+
"Migration aborted: These need to be approved (and run) or denied before upgrading to Nautobot v3, as the introduction of the approval workflows feature means that future scheduled-job approvals will be handled differently.",
|
|
27
|
+
"Refer to the documentation: https://docs.nautobot.com/projects/core/en/v2.4.14/user-guide/platform-functionality/jobs/job-scheduling-and-approvals/#approval-via-the-ui",
|
|
28
|
+
"Below is a list of affected scheduled jobs:",
|
|
29
|
+
]
|
|
30
|
+
for schedule_job_id, scheduled_job_name in approval_required_scheduled_jobs:
|
|
31
|
+
message_lines.append(f" - ID: {schedule_job_id}, Name: {scheduled_job_name}")
|
|
32
|
+
raise ApprovalRequiredScheduledJobsError("\n".join(message_lines))
|
|
33
|
+
|
|
34
|
+
Job = apps.get_model("extras", "Job")
|
|
35
|
+
approval_required_jobs = Job.objects.filter(approval_required=True).values_list("name")
|
|
36
|
+
if approval_required_jobs:
|
|
37
|
+
message_lines = [
|
|
38
|
+
"Migration passed, but the following jobs still have `approval_required=True`.",
|
|
39
|
+
"These jobs will no longer trigger approval automatically.",
|
|
40
|
+
"After upgrading to Nautobot 3.x, you should add an approval workflow definition(s) covering these jobs.",
|
|
41
|
+
"Refer to the documentation: https://docs.nautobot.com/projects/core/en/v3.0.0/user-guide/platform-functionality/approval-workflow/",
|
|
42
|
+
"Affected jobs (Names):",
|
|
43
|
+
]
|
|
44
|
+
for job_name in approval_required_jobs:
|
|
45
|
+
message_lines.append(f" - {job_name}")
|
|
46
|
+
print("\n".join(message_lines))
|
|
47
|
+
else:
|
|
48
|
+
print("Migration passed: No approval_required jobs or scheduled jobs found.")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Migration(migrations.Migration):
|
|
52
|
+
dependencies = [
|
|
53
|
+
("extras", "0125_jobresult_date_started"),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
operations = [
|
|
57
|
+
migrations.RunPython(code=_migrate_data, reverse_code=migrations.RunPython.noop),
|
|
58
|
+
]
|