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
|
@@ -36,6 +36,7 @@ from nautobot.core.models.querysets import RestrictedQuerySet
|
|
|
36
36
|
from nautobot.core.models.validators import validate_regex
|
|
37
37
|
from nautobot.core.settings_funcs import is_truthy
|
|
38
38
|
from nautobot.core.templatetags.helpers import render_markdown
|
|
39
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
39
40
|
from nautobot.core.utils.data import render_jinja2, validate_jinja2
|
|
40
41
|
from nautobot.extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices
|
|
41
42
|
from nautobot.extras.models import ChangeLoggedModel
|
|
@@ -56,8 +57,12 @@ class ComputedFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
56
57
|
Returns a queryset by default, or a list if `get_queryset` param is False.
|
|
57
58
|
"""
|
|
58
59
|
concrete_model = model._meta.concrete_model
|
|
59
|
-
cache_key =
|
|
60
|
-
|
|
60
|
+
cache_key = construct_cache_key(
|
|
61
|
+
self, method_name="get_for_model", branch_aware=True, model=concrete_model._meta.label_lower
|
|
62
|
+
)
|
|
63
|
+
list_cache_key = construct_cache_key(
|
|
64
|
+
self, method_name="get_for_model", branch_aware=True, model=concrete_model._meta.label_lower, listing=True
|
|
65
|
+
)
|
|
61
66
|
if not get_queryset:
|
|
62
67
|
listing = cache.get(list_cache_key)
|
|
63
68
|
if listing is not None:
|
|
@@ -73,8 +78,6 @@ class ComputedFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
73
78
|
return listing
|
|
74
79
|
return queryset
|
|
75
80
|
|
|
76
|
-
get_for_model.cache_key_prefix = "nautobot.extras.computedfield.get_for_model"
|
|
77
|
-
|
|
78
81
|
def populate_list_caches(self):
|
|
79
82
|
"""Populate all caches for `get_for_model(..., get_queryset=False)` lookups."""
|
|
80
83
|
queryset = self.all().select_related("content_type")
|
|
@@ -83,7 +86,10 @@ class ComputedFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
83
86
|
listings[f"{cf.content_type.app_label}.{cf.content_type.model}"].append(cf)
|
|
84
87
|
for ct in ContentType.objects.all():
|
|
85
88
|
label = f"{ct.app_label}.{ct.model}"
|
|
86
|
-
|
|
89
|
+
cache_key = construct_cache_key(
|
|
90
|
+
self, method_name="get_for_model", branch_aware=True, model=label, listing=True
|
|
91
|
+
)
|
|
92
|
+
cache.set(cache_key, listings[label])
|
|
87
93
|
|
|
88
94
|
def bulk_create(self, objs, *args, **kwargs):
|
|
89
95
|
"""Validate templates before saving."""
|
|
@@ -437,10 +443,21 @@ class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
437
443
|
get_queryset (bool): Whether to return a QuerySet or a list.
|
|
438
444
|
"""
|
|
439
445
|
concrete_model = model._meta.concrete_model
|
|
440
|
-
cache_key = (
|
|
441
|
-
|
|
446
|
+
cache_key = construct_cache_key(
|
|
447
|
+
self,
|
|
448
|
+
method_name="get_for_model",
|
|
449
|
+
branch_aware=True,
|
|
450
|
+
model=concrete_model._meta.label_lower,
|
|
451
|
+
exclude_filter_disabled=exclude_filter_disabled,
|
|
452
|
+
)
|
|
453
|
+
list_cache_key = construct_cache_key(
|
|
454
|
+
self,
|
|
455
|
+
method_name="get_for_model",
|
|
456
|
+
branch_aware=True,
|
|
457
|
+
model=concrete_model._meta.label_lower,
|
|
458
|
+
exclude_filter_disabled=exclude_filter_disabled,
|
|
459
|
+
listing=True,
|
|
442
460
|
)
|
|
443
|
-
list_cache_key = f"{cache_key}.list"
|
|
444
461
|
if not get_queryset:
|
|
445
462
|
listing = cache.get(list_cache_key)
|
|
446
463
|
if listing is not None:
|
|
@@ -458,20 +475,18 @@ class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
458
475
|
return listing
|
|
459
476
|
return queryset
|
|
460
477
|
|
|
461
|
-
get_for_model.cache_key_prefix = "nautobot.extras.customfield.get_for_model"
|
|
462
|
-
|
|
463
478
|
def keys_for_model(self, model):
|
|
464
479
|
"""Return list of all keys for CustomFields assigned to the given model."""
|
|
465
480
|
concrete_model = model._meta.concrete_model
|
|
466
|
-
cache_key =
|
|
481
|
+
cache_key = construct_cache_key(
|
|
482
|
+
self, method_name="keys_for_model", branch_aware=True, model=concrete_model._meta.label_lower
|
|
483
|
+
)
|
|
467
484
|
keys = cache.get(cache_key)
|
|
468
485
|
if keys is None:
|
|
469
486
|
keys = list(self.get_for_model(model).values_list("key", flat=True))
|
|
470
487
|
cache.set(cache_key, keys)
|
|
471
488
|
return keys
|
|
472
489
|
|
|
473
|
-
keys_for_model.cache_key_prefix = "nautobot.extras.customfield.keys_for_model"
|
|
474
|
-
|
|
475
490
|
def populate_list_caches(self):
|
|
476
491
|
"""Populate all caches for `get_for_model(..., get_queryset=False)` and `keys_for_model` lookups."""
|
|
477
492
|
queryset = self.all().prefetch_related("content_types")
|
|
@@ -486,9 +501,32 @@ class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
486
501
|
key_listings[label].append(cf.key)
|
|
487
502
|
for ct in ContentType.objects.all():
|
|
488
503
|
label = f"{ct.app_label}.{ct.model}"
|
|
489
|
-
cache.set(
|
|
490
|
-
|
|
491
|
-
|
|
504
|
+
cache.set(
|
|
505
|
+
construct_cache_key(
|
|
506
|
+
self,
|
|
507
|
+
method_name="get_for_model",
|
|
508
|
+
branch_aware=True,
|
|
509
|
+
model=label,
|
|
510
|
+
exclude_filter_disabled=True,
|
|
511
|
+
listing=True,
|
|
512
|
+
),
|
|
513
|
+
cf_listings[label][True],
|
|
514
|
+
)
|
|
515
|
+
cache.set(
|
|
516
|
+
construct_cache_key(
|
|
517
|
+
self,
|
|
518
|
+
method_name="get_for_model",
|
|
519
|
+
branch_aware=True,
|
|
520
|
+
model=label,
|
|
521
|
+
exclude_filter_disabled=True,
|
|
522
|
+
listing=False,
|
|
523
|
+
),
|
|
524
|
+
cf_listings[label][False],
|
|
525
|
+
)
|
|
526
|
+
cache.set(
|
|
527
|
+
construct_cache_key(self, method_name="keys_for_model", branch_aware=True, model=label),
|
|
528
|
+
key_listings[label],
|
|
529
|
+
)
|
|
492
530
|
|
|
493
531
|
|
|
494
532
|
@extras_features("webhooks")
|
|
@@ -607,10 +645,6 @@ class CustomField(
|
|
|
607
645
|
def __str__(self):
|
|
608
646
|
return self.label
|
|
609
647
|
|
|
610
|
-
@property
|
|
611
|
-
def choices_cache_key(self):
|
|
612
|
-
return f"nautobot.extras.customfield.choices.{self.pk}"
|
|
613
|
-
|
|
614
648
|
@property
|
|
615
649
|
def choices(self) -> list[str]:
|
|
616
650
|
"""
|
|
@@ -621,10 +655,11 @@ class CustomField(
|
|
|
621
655
|
"""
|
|
622
656
|
if self.type not in [CustomFieldTypeChoices.TYPE_SELECT, CustomFieldTypeChoices.TYPE_MULTISELECT]:
|
|
623
657
|
return []
|
|
624
|
-
|
|
658
|
+
cache_key = construct_cache_key(self, method_name="choices", branch_aware=True)
|
|
659
|
+
choices = cache.get(cache_key)
|
|
625
660
|
if choices is None:
|
|
626
661
|
choices = list(self.custom_field_choices.order_by("weight", "value").values_list("value", flat=True))
|
|
627
|
-
cache.set(
|
|
662
|
+
cache.set(cache_key, choices)
|
|
628
663
|
return choices
|
|
629
664
|
|
|
630
665
|
def save(self, *args, **kwargs):
|
|
@@ -7,14 +7,18 @@ import shutil
|
|
|
7
7
|
import tempfile
|
|
8
8
|
|
|
9
9
|
from django.conf import settings
|
|
10
|
+
from django.core.cache import cache
|
|
10
11
|
from django.core.exceptions import ValidationError
|
|
11
12
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
12
13
|
from django.db import models
|
|
13
14
|
|
|
14
15
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
16
|
+
from nautobot.core.models import BaseManager
|
|
15
17
|
from nautobot.core.models.fields import AutoSlugField, LaxURLField, slugify_dashes_to_underscores
|
|
16
18
|
from nautobot.core.models.generics import PrimaryModel
|
|
19
|
+
from nautobot.core.models.querysets import RestrictedQuerySet
|
|
17
20
|
from nautobot.core.models.validators import EnhancedURLValidator
|
|
21
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
18
22
|
from nautobot.core.utils.git import GitRepo
|
|
19
23
|
from nautobot.core.utils.module_loading import check_name_safe_to_import_privately
|
|
20
24
|
from nautobot.extras.utils import extras_features
|
|
@@ -22,6 +26,21 @@ from nautobot.extras.utils import extras_features
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
24
28
|
|
|
29
|
+
class GitRepositoryManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
30
|
+
def get_for_provided_contents(self, provided_contents_type):
|
|
31
|
+
cache_key = construct_cache_key(
|
|
32
|
+
self,
|
|
33
|
+
method_name="get_for_provided_contents",
|
|
34
|
+
branch_aware=True,
|
|
35
|
+
provided_contents_type=provided_contents_type,
|
|
36
|
+
)
|
|
37
|
+
queryset = cache.get(cache_key)
|
|
38
|
+
if queryset is None:
|
|
39
|
+
queryset = self.get_queryset().filter(provided_contents__contains=provided_contents_type)
|
|
40
|
+
cache.set(cache_key, queryset)
|
|
41
|
+
return queryset
|
|
42
|
+
|
|
43
|
+
|
|
25
44
|
@extras_features(
|
|
26
45
|
"config_context_owners",
|
|
27
46
|
"export_template_owners",
|
|
@@ -75,6 +94,8 @@ class GitRepository(PrimaryModel):
|
|
|
75
94
|
# the data types registered in registry['datasource_contents'].
|
|
76
95
|
provided_contents = models.JSONField(encoder=DjangoJSONEncoder, default=list, blank=True)
|
|
77
96
|
|
|
97
|
+
objects = GitRepositoryManager()
|
|
98
|
+
|
|
78
99
|
clone_fields = ["remote_url", "secrets_group", "provided_contents"]
|
|
79
100
|
|
|
80
101
|
class Meta:
|
nautobot/extras/models/groups.py
CHANGED
|
@@ -76,6 +76,7 @@ class DynamicGroup(PrimaryModel):
|
|
|
76
76
|
|
|
77
77
|
objects = BaseManager.from_queryset(DynamicGroupQuerySet)()
|
|
78
78
|
is_dynamic_group_associable_model = False
|
|
79
|
+
is_data_compliance_model = False
|
|
79
80
|
|
|
80
81
|
clone_fields = ["content_type", "group_type", "filter", "tenant"]
|
|
81
82
|
|
|
@@ -1255,6 +1256,7 @@ class StaticGroupAssociation(OrganizationalModel):
|
|
|
1255
1256
|
is_contact_associable_model = False
|
|
1256
1257
|
is_dynamic_group_associable_model = False
|
|
1257
1258
|
is_saved_view_model = False
|
|
1259
|
+
is_data_compliance_model = False
|
|
1258
1260
|
|
|
1259
1261
|
class Meta:
|
|
1260
1262
|
unique_together = [["dynamic_group", "associated_object_type", "associated_object_id"]]
|
nautobot/extras/models/jobs.py
CHANGED
|
@@ -15,7 +15,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
15
15
|
from django.core.exceptions import ValidationError
|
|
16
16
|
from django.core.validators import MinValueValidator
|
|
17
17
|
from django.db import models, transaction
|
|
18
|
-
from django.db.models import ProtectedError, signals
|
|
18
|
+
from django.db.models import Count, ProtectedError, Q, signals
|
|
19
19
|
from django.utils import timezone
|
|
20
20
|
from django.utils.functional import cached_property
|
|
21
21
|
from django_celery_beat.clockedschedule import clocked
|
|
@@ -29,8 +29,10 @@ from nautobot.core.celery import (
|
|
|
29
29
|
setup_nautobot_job_logging,
|
|
30
30
|
)
|
|
31
31
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
32
|
+
from nautobot.core.events import publish_event
|
|
32
33
|
from nautobot.core.models import BaseManager, BaseModel
|
|
33
34
|
from nautobot.core.models.generics import OrganizationalModel, PrimaryModel
|
|
35
|
+
from nautobot.core.models.utils import serialize_object_v2
|
|
34
36
|
from nautobot.core.utils.logging import sanitize
|
|
35
37
|
from nautobot.extras.choices import (
|
|
36
38
|
ButtonClassChoices,
|
|
@@ -48,7 +50,13 @@ from nautobot.extras.constants import (
|
|
|
48
50
|
)
|
|
49
51
|
from nautobot.extras.managers import JobResultManager, ScheduledJobsManager
|
|
50
52
|
from nautobot.extras.models import ChangeLoggedModel, GitRepository
|
|
51
|
-
from nautobot.extras.models.mixins import
|
|
53
|
+
from nautobot.extras.models.mixins import (
|
|
54
|
+
ApprovableModelMixin,
|
|
55
|
+
ContactMixin,
|
|
56
|
+
DynamicGroupsModelMixin,
|
|
57
|
+
NotesMixin,
|
|
58
|
+
SavedViewMixin,
|
|
59
|
+
)
|
|
52
60
|
from nautobot.extras.querysets import JobQuerySet, ScheduledJobExtendedQuerySet
|
|
53
61
|
from nautobot.extras.utils import (
|
|
54
62
|
ChangeLoggedModelsQuery,
|
|
@@ -153,9 +161,6 @@ class Job(PrimaryModel):
|
|
|
153
161
|
|
|
154
162
|
# Additional properties, potentially inherited from the source code
|
|
155
163
|
# See also the docstring of nautobot.extras.jobs.BaseJob.Meta.
|
|
156
|
-
approval_required = models.BooleanField(
|
|
157
|
-
default=False, help_text="Whether the job requires approval from another user before running"
|
|
158
|
-
)
|
|
159
164
|
hidden = models.BooleanField(
|
|
160
165
|
default=False,
|
|
161
166
|
db_index=True,
|
|
@@ -214,10 +219,6 @@ class Job(PrimaryModel):
|
|
|
214
219
|
default=False,
|
|
215
220
|
help_text="If set, the configured description will remain even if the underlying Job source code changes",
|
|
216
221
|
)
|
|
217
|
-
approval_required_override = models.BooleanField(
|
|
218
|
-
default=False,
|
|
219
|
-
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
220
|
-
)
|
|
221
222
|
dryrun_default_override = models.BooleanField(
|
|
222
223
|
default=False,
|
|
223
224
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
@@ -251,6 +252,7 @@ class Job(PrimaryModel):
|
|
|
251
252
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
252
253
|
)
|
|
253
254
|
objects = BaseManager.from_queryset(JobQuerySet)()
|
|
255
|
+
is_data_compliance_model = False
|
|
254
256
|
|
|
255
257
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
256
258
|
|
|
@@ -314,7 +316,7 @@ class Job(PrimaryModel):
|
|
|
314
316
|
|
|
315
317
|
@property
|
|
316
318
|
def runnable(self):
|
|
317
|
-
return self.enabled and self.installed
|
|
319
|
+
return self.enabled and self.installed
|
|
318
320
|
|
|
319
321
|
@cached_property
|
|
320
322
|
def git_repository(self):
|
|
@@ -389,11 +391,6 @@ class Job(PrimaryModel):
|
|
|
389
391
|
if len(self.name) > JOB_MAX_NAME_LENGTH:
|
|
390
392
|
raise ValidationError(f"Name may not exceed {JOB_MAX_NAME_LENGTH} characters in length")
|
|
391
393
|
|
|
392
|
-
if self.has_sensitive_variables is True and self.approval_required is True:
|
|
393
|
-
raise ValidationError(
|
|
394
|
-
{"approval_required": "A job that may have sensitive variables cannot be marked as requiring approval"}
|
|
395
|
-
)
|
|
396
|
-
|
|
397
394
|
def save(self, *args, **kwargs):
|
|
398
395
|
"""When a Job is uninstalled, auto-disable all associated JobButtons, JobHooks, and ScheduledJobs."""
|
|
399
396
|
super().save(*args, **kwargs)
|
|
@@ -443,6 +440,8 @@ class JobHook(OrganizationalModel):
|
|
|
443
440
|
type_delete = models.BooleanField(default=False, help_text="Call this job hook when a matching object is deleted.")
|
|
444
441
|
type_update = models.BooleanField(default=False, help_text="Call this job hook when a matching object is updated.")
|
|
445
442
|
|
|
443
|
+
is_data_compliance_model = False
|
|
444
|
+
|
|
446
445
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/jobhook.html"
|
|
447
446
|
|
|
448
447
|
class Meta:
|
|
@@ -533,8 +532,10 @@ class JobLogEntry(BaseModel):
|
|
|
533
532
|
absolute_url = models.CharField(max_length=JOB_LOG_MAX_ABSOLUTE_URL_LENGTH, blank=True, default="")
|
|
534
533
|
|
|
535
534
|
is_metadata_associable_model = False
|
|
535
|
+
is_data_compliance_model = False
|
|
536
536
|
|
|
537
537
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
538
|
+
hide_in_diff_view = True
|
|
538
539
|
|
|
539
540
|
def __str__(self):
|
|
540
541
|
return self.message
|
|
@@ -583,6 +584,7 @@ class JobQueue(PrimaryModel):
|
|
|
583
584
|
)
|
|
584
585
|
|
|
585
586
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/jobqueue.html"
|
|
587
|
+
is_data_compliance_model = False
|
|
586
588
|
|
|
587
589
|
class Meta:
|
|
588
590
|
ordering = ["name"]
|
|
@@ -613,6 +615,7 @@ class JobQueueAssignment(BaseModel):
|
|
|
613
615
|
job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name="job_queue_assignments")
|
|
614
616
|
job_queue = models.ForeignKey(JobQueue, on_delete=models.CASCADE, related_name="job_assignments")
|
|
615
617
|
is_metadata_associable_model = False
|
|
618
|
+
is_data_compliance_model = False
|
|
616
619
|
|
|
617
620
|
class Meta:
|
|
618
621
|
unique_together = ["job", "job_queue"]
|
|
@@ -681,10 +684,17 @@ class JobResult(SavedViewMixin, BaseModel, CustomFieldModel):
|
|
|
681
684
|
traceback = models.TextField(blank=True, null=True) # noqa: DJ001 # django-nullable-model-string-field -- TODO: can we remove null=True?
|
|
682
685
|
meta = models.JSONField(null=True, default=None, editable=False)
|
|
683
686
|
scheduled_job = models.ForeignKey(to="extras.ScheduledJob", on_delete=models.SET_NULL, null=True, blank=True)
|
|
687
|
+
debug_log_count = models.PositiveIntegerField(blank=True, null=True, editable=False)
|
|
688
|
+
success_log_count = models.PositiveIntegerField(blank=True, null=True, editable=False)
|
|
689
|
+
info_log_count = models.PositiveIntegerField(blank=True, null=True, editable=False)
|
|
690
|
+
warning_log_count = models.PositiveIntegerField(blank=True, null=True, editable=False)
|
|
691
|
+
error_log_count = models.PositiveIntegerField(blank=True, null=True, editable=False)
|
|
684
692
|
|
|
685
693
|
objects = JobResultManager()
|
|
686
694
|
|
|
687
695
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
696
|
+
hide_in_diff_view = True
|
|
697
|
+
is_data_compliance_model = False
|
|
688
698
|
|
|
689
699
|
def __init__(self, *args, **kwargs):
|
|
690
700
|
super().__init__(*args, **kwargs)
|
|
@@ -764,6 +774,30 @@ class JobResult(SavedViewMixin, BaseModel, CustomFieldModel):
|
|
|
764
774
|
|
|
765
775
|
set_status.alters_data = True
|
|
766
776
|
|
|
777
|
+
def count_logs_by_level(self):
|
|
778
|
+
"""Helper method to count JobLogEntries after a Job is run, or update these values when missing or changed."""
|
|
779
|
+
db_log_counts = self.job_log_entries.aggregate(
|
|
780
|
+
debug_log_count=Count("pk", filter=Q(log_level=LogLevelChoices.LOG_DEBUG)),
|
|
781
|
+
success_log_count=Count("pk", filter=Q(log_level=LogLevelChoices.LOG_SUCCESS)),
|
|
782
|
+
info_log_count=Count("pk", filter=Q(log_level=LogLevelChoices.LOG_INFO)),
|
|
783
|
+
warning_log_count=Count("pk", filter=Q(log_level=LogLevelChoices.LOG_WARNING)),
|
|
784
|
+
error_log_count=Count(
|
|
785
|
+
"pk",
|
|
786
|
+
filter=Q(
|
|
787
|
+
log_level__in=[
|
|
788
|
+
LogLevelChoices.LOG_FAILURE,
|
|
789
|
+
LogLevelChoices.LOG_ERROR,
|
|
790
|
+
LogLevelChoices.LOG_CRITICAL,
|
|
791
|
+
]
|
|
792
|
+
),
|
|
793
|
+
),
|
|
794
|
+
)
|
|
795
|
+
self.debug_log_count = db_log_counts["debug_log_count"]
|
|
796
|
+
self.success_log_count = db_log_counts["success_log_count"]
|
|
797
|
+
self.info_log_count = db_log_counts["info_log_count"]
|
|
798
|
+
self.warning_log_count = db_log_counts["warning_log_count"]
|
|
799
|
+
self.error_log_count = db_log_counts["error_log_count"]
|
|
800
|
+
|
|
767
801
|
@classmethod
|
|
768
802
|
def execute_job(cls, *args, **kwargs):
|
|
769
803
|
"""
|
|
@@ -862,6 +896,7 @@ class JobResult(SavedViewMixin, BaseModel, CustomFieldModel):
|
|
|
862
896
|
# And from the kubernetes pod, we specify "--local"/synchronous=True
|
|
863
897
|
# so that `run_kubernetes_job_and_return_job_result` is not executed again and the job will be run locally.
|
|
864
898
|
if job_queue.queue_type == JobQueueTypeChoices.TYPE_KUBERNETES and not synchronous:
|
|
899
|
+
# TODO: make this branch aware!
|
|
865
900
|
return run_kubernetes_job_and_return_job_result(job_queue, job_result, json.dumps(job_kwargs))
|
|
866
901
|
|
|
867
902
|
job_celery_kwargs = {
|
|
@@ -1007,6 +1042,18 @@ class JobResult(SavedViewMixin, BaseModel, CustomFieldModel):
|
|
|
1007
1042
|
|
|
1008
1043
|
log.alters_data = True
|
|
1009
1044
|
|
|
1045
|
+
def save(self, *args, **kwargs):
|
|
1046
|
+
"""When a JobResult is saved and in a terminal state, store missing log counts for summary."""
|
|
1047
|
+
if self.status in JobResultStatusChoices.READY_STATES and None in [
|
|
1048
|
+
self.debug_log_count,
|
|
1049
|
+
self.info_log_count,
|
|
1050
|
+
self.success_log_count,
|
|
1051
|
+
self.warning_log_count,
|
|
1052
|
+
self.error_log_count,
|
|
1053
|
+
]:
|
|
1054
|
+
self.count_logs_by_level()
|
|
1055
|
+
super().save(*args, **kwargs)
|
|
1056
|
+
|
|
1010
1057
|
|
|
1011
1058
|
#
|
|
1012
1059
|
# Job Button
|
|
@@ -1056,10 +1103,18 @@ class JobButton(ContactMixin, ChangeLoggedModel, DynamicGroupsModelMixin, NotesM
|
|
|
1056
1103
|
)
|
|
1057
1104
|
|
|
1058
1105
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/jobbutton.html"
|
|
1106
|
+
is_data_compliance_model = False
|
|
1059
1107
|
|
|
1060
1108
|
class Meta:
|
|
1061
1109
|
ordering = ["group_name", "weight", "name"]
|
|
1062
1110
|
|
|
1111
|
+
@property
|
|
1112
|
+
def button_class_css_class(self):
|
|
1113
|
+
"""Map self.button_class database value to the correct CSS class for buttons."""
|
|
1114
|
+
if self.button_class == ButtonClassChoices.CLASS_DEFAULT:
|
|
1115
|
+
return "secondary"
|
|
1116
|
+
return self.button_class
|
|
1117
|
+
|
|
1063
1118
|
def __str__(self):
|
|
1064
1119
|
return self.name
|
|
1065
1120
|
|
|
@@ -1082,6 +1137,7 @@ class ScheduledJobs(models.Model):
|
|
|
1082
1137
|
last_update = models.DateTimeField(null=False)
|
|
1083
1138
|
|
|
1084
1139
|
objects = ScheduledJobsManager()
|
|
1140
|
+
is_data_compliance_model = False
|
|
1085
1141
|
|
|
1086
1142
|
def __str__(self):
|
|
1087
1143
|
return str(self.ident)
|
|
@@ -1114,7 +1170,7 @@ class ScheduledJobs(models.Model):
|
|
|
1114
1170
|
return None
|
|
1115
1171
|
|
|
1116
1172
|
|
|
1117
|
-
class ScheduledJob(BaseModel):
|
|
1173
|
+
class ScheduledJob(ApprovableModelMixin, BaseModel):
|
|
1118
1174
|
"""Model representing a periodic task."""
|
|
1119
1175
|
|
|
1120
1176
|
name = models.CharField(
|
|
@@ -1199,22 +1255,14 @@ class ScheduledJob(BaseModel):
|
|
|
1199
1255
|
null=True,
|
|
1200
1256
|
help_text="User that requested the schedule",
|
|
1201
1257
|
)
|
|
1202
|
-
approved_by_user = models.ForeignKey(
|
|
1203
|
-
to=settings.AUTH_USER_MODEL,
|
|
1204
|
-
on_delete=models.SET_NULL,
|
|
1205
|
-
related_name="+",
|
|
1206
|
-
blank=True,
|
|
1207
|
-
null=True,
|
|
1208
|
-
help_text="User that approved the schedule",
|
|
1209
|
-
)
|
|
1210
1258
|
# todoindex:
|
|
1211
1259
|
approval_required = models.BooleanField(default=False)
|
|
1212
|
-
|
|
1260
|
+
decision_date = models.DateTimeField(
|
|
1213
1261
|
editable=False,
|
|
1214
1262
|
blank=True,
|
|
1215
1263
|
null=True,
|
|
1216
|
-
verbose_name="Approval date/time",
|
|
1217
|
-
help_text="Datetime that the schedule was approved",
|
|
1264
|
+
verbose_name="Approval/Rejection date/time",
|
|
1265
|
+
help_text="Datetime that the schedule was approved or denied",
|
|
1218
1266
|
)
|
|
1219
1267
|
crontab = models.CharField(
|
|
1220
1268
|
max_length=CHARFIELD_MAX_LENGTH,
|
|
@@ -1227,6 +1275,7 @@ class ScheduledJob(BaseModel):
|
|
|
1227
1275
|
no_changes = False
|
|
1228
1276
|
|
|
1229
1277
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html"
|
|
1278
|
+
is_data_compliance_model = False
|
|
1230
1279
|
|
|
1231
1280
|
def __str__(self):
|
|
1232
1281
|
return f"{self.name}: {self.interval}"
|
|
@@ -1257,18 +1306,43 @@ class ScheduledJob(BaseModel):
|
|
|
1257
1306
|
self.last_run_at = self.start_time - timedelta(
|
|
1258
1307
|
**{JobExecutionType.CELERY_INTERVAL_MAP[self.interval]: multiplier},
|
|
1259
1308
|
)
|
|
1260
|
-
|
|
1309
|
+
is_new = not self.present_in_database
|
|
1261
1310
|
super().save(*args, **kwargs)
|
|
1311
|
+
if is_new:
|
|
1312
|
+
self.begin_approval_workflow()
|
|
1262
1313
|
|
|
1263
|
-
def
|
|
1314
|
+
def on_workflow_initiated(self, approval_workflow):
|
|
1315
|
+
"""When initiated, set approval required to True."""
|
|
1316
|
+
self.approval_required = True
|
|
1317
|
+
self.save()
|
|
1318
|
+
|
|
1319
|
+
def on_workflow_approved(self, approval_workflow):
|
|
1320
|
+
"""When approved, set decision_date to decision_date from approval workflow."""
|
|
1321
|
+
self.decision_date = approval_workflow.decision_date
|
|
1322
|
+
self.save()
|
|
1323
|
+
|
|
1324
|
+
publish_event_payload = {"data": serialize_object_v2(self)}
|
|
1325
|
+
publish_event(topic="nautobot.jobs.approval.approved", payload=publish_event_payload)
|
|
1326
|
+
|
|
1327
|
+
def on_workflow_denied(self, approval_workflow):
|
|
1328
|
+
"""When denied, set decision_date to decision_date from approval workflow."""
|
|
1329
|
+
self.decision_date = approval_workflow.decision_date
|
|
1330
|
+
self.save()
|
|
1331
|
+
|
|
1332
|
+
publish_event_payload = {"data": serialize_object_v2(self)}
|
|
1333
|
+
publish_event(topic="nautobot.jobs.approval.denied", payload=publish_event_payload)
|
|
1334
|
+
|
|
1335
|
+
def get_approval_template(self):
|
|
1264
1336
|
"""
|
|
1265
|
-
|
|
1337
|
+
Return a custom template path to be used for the approval UI.
|
|
1338
|
+
|
|
1339
|
+
This allows the object to override the default approval template
|
|
1340
|
+
when special logic or warnings are needed. If no override is
|
|
1341
|
+
required, return None to use the standard approval form.
|
|
1266
1342
|
"""
|
|
1267
|
-
if self.
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
if bool(self.approved_by_user) ^ bool(self.approved_at):
|
|
1271
|
-
raise ValidationError("Approval by user and approval time must either both be set or both be undefined")
|
|
1343
|
+
if self.one_off and self.start_time < timezone.now():
|
|
1344
|
+
return "extras/job_approval_confirmation.html"
|
|
1345
|
+
return None
|
|
1272
1346
|
|
|
1273
1347
|
@property
|
|
1274
1348
|
def schedule(self):
|
|
@@ -1283,6 +1357,10 @@ class ScheduledJob(BaseModel):
|
|
|
1283
1357
|
"""Deprecated backward-compatibility property for the queue name this job is scheduled for."""
|
|
1284
1358
|
return self.job_queue.name if self.job_queue else ""
|
|
1285
1359
|
|
|
1360
|
+
@property
|
|
1361
|
+
def runnable(self):
|
|
1362
|
+
return self.job_model.runnable and not (self.job_model.has_sensitive_variables and self.approval_required)
|
|
1363
|
+
|
|
1286
1364
|
@queue.setter
|
|
1287
1365
|
def queue(self, value: str):
|
|
1288
1366
|
if value:
|
|
@@ -1332,10 +1410,10 @@ class ScheduledJob(BaseModel):
|
|
|
1332
1410
|
interval: str = JobExecutionType.TYPE_IMMEDIATELY,
|
|
1333
1411
|
crontab: str = "",
|
|
1334
1412
|
profile: bool = False,
|
|
1335
|
-
approval_required: bool = False,
|
|
1336
1413
|
job_queue: Optional[JobQueue] = None,
|
|
1337
1414
|
task_queue: Optional[str] = None, # deprecated!
|
|
1338
1415
|
ignore_singleton_lock: bool = False,
|
|
1416
|
+
validated_save: bool = True,
|
|
1339
1417
|
**job_kwargs,
|
|
1340
1418
|
):
|
|
1341
1419
|
"""
|
|
@@ -1354,7 +1432,6 @@ class ScheduledJob(BaseModel):
|
|
|
1354
1432
|
Defaults to JobExecutionType.TYPE_IMMEDIATELY.
|
|
1355
1433
|
crontab (str): The crontab string for the schedule. Defaults to "".
|
|
1356
1434
|
profile (bool): Flag indicating whether to profile the job. Defaults to False.
|
|
1357
|
-
approval_required (bool): Flag indicating if approval is required. Defaults to False.
|
|
1358
1435
|
job_queue (JobQueue): The Job queue to use. If unset, use the configured default celery queue.
|
|
1359
1436
|
task_queue (str): The queue name to use. **Deprecated, prefer `job_queue`.**
|
|
1360
1437
|
ignore_singleton_lock (bool): Whether to ignore singleton locks. Defaults to False.
|
|
@@ -1388,6 +1465,12 @@ class ScheduledJob(BaseModel):
|
|
|
1388
1465
|
"queue": task_queue,
|
|
1389
1466
|
"nautobot_job_ignore_singleton_lock": ignore_singleton_lock,
|
|
1390
1467
|
}
|
|
1468
|
+
if "nautobot_version_control" in settings.PLUGINS:
|
|
1469
|
+
from nautobot_version_control.utils import active_branch # pylint: disable=import-error
|
|
1470
|
+
|
|
1471
|
+
branch_name = active_branch()
|
|
1472
|
+
# TODO: what do we do when merging a branch's ScheduledJob down to main?
|
|
1473
|
+
celery_kwargs["nautobot_job_branch_name"] = branch_name
|
|
1391
1474
|
if job_model.soft_time_limit > 0:
|
|
1392
1475
|
celery_kwargs["soft_time_limit"] = job_model.soft_time_limit
|
|
1393
1476
|
if job_model.time_limit > 0:
|
|
@@ -1417,11 +1500,11 @@ class ScheduledJob(BaseModel):
|
|
|
1417
1500
|
interval=interval,
|
|
1418
1501
|
one_off=(interval == JobExecutionType.TYPE_FUTURE),
|
|
1419
1502
|
user=user,
|
|
1420
|
-
approval_required=approval_required,
|
|
1421
1503
|
crontab=crontab,
|
|
1422
1504
|
job_queue=job_queue,
|
|
1423
1505
|
)
|
|
1424
|
-
|
|
1506
|
+
if validated_save:
|
|
1507
|
+
scheduled_job.validated_save()
|
|
1425
1508
|
return scheduled_job
|
|
1426
1509
|
|
|
1427
1510
|
create_schedule.__func__.alters_data = True
|
|
@@ -12,6 +12,7 @@ from nautobot.core.models.fields import JSONArrayField
|
|
|
12
12
|
from nautobot.core.models.generics import PrimaryModel
|
|
13
13
|
from nautobot.core.models.querysets import RestrictedQuerySet
|
|
14
14
|
from nautobot.core.settings_funcs import is_truthy
|
|
15
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
15
16
|
from nautobot.extras.choices import MetadataTypeDataTypeChoices
|
|
16
17
|
from nautobot.extras.models.change_logging import ChangeLoggedModel
|
|
17
18
|
from nautobot.extras.models.contacts import Contact, Team
|
|
@@ -24,7 +25,7 @@ class MetadataTypeManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
24
25
|
def get_for_model(self, model):
|
|
25
26
|
"""Return all MetadataTypes assigned to the given model."""
|
|
26
27
|
concrete_model = model._meta.concrete_model
|
|
27
|
-
cache_key =
|
|
28
|
+
cache_key = construct_cache_key(self, method_name="get_for_model", model=concrete_model._meta.label_lower)
|
|
28
29
|
queryset = cache.get(cache_key)
|
|
29
30
|
if queryset is None:
|
|
30
31
|
content_type = ContentType.objects.get_for_model(concrete_model)
|
|
@@ -32,8 +33,6 @@ class MetadataTypeManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
32
33
|
cache.set(cache_key, queryset)
|
|
33
34
|
return queryset
|
|
34
35
|
|
|
35
|
-
get_for_model.cache_key_prefix = "nautobot.extras.metadatatype.get_for_model"
|
|
36
|
-
|
|
37
36
|
|
|
38
37
|
@extras_features(
|
|
39
38
|
"custom_links",
|