nautobot 3.0.0a2__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nautobot/apps/choices.py +4 -2
- nautobot/apps/filters.py +7 -9
- nautobot/apps/models.py +2 -2
- nautobot/apps/ui.py +13 -1
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/filters.py +3 -2
- nautobot/circuits/navigation.py +3 -2
- nautobot/circuits/templates/circuits/circuit_create.html +3 -3
- nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
- nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
- nautobot/circuits/tests/integration/test_circuit.py +10 -13
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/cloud/filters.py +1 -1
- nautobot/cloud/navigation.py +3 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +6 -1
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +12 -0
- nautobot/core/apps/__init__.py +11 -10
- nautobot/core/celery/__init__.py +3 -5
- nautobot/core/checks.py +46 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
- nautobot/core/cli/migrate_deprecated_templates.py +227 -0
- nautobot/core/constants.py +3 -0
- nautobot/core/context_processors.py +9 -1
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/forms.py +1 -1
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +62 -3
- nautobot/core/jobs/groups.py +31 -1
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/tree_queries.py +10 -5
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +35 -19
- nautobot/core/settings.yaml +17 -33
- nautobot/core/signals.py +12 -1
- nautobot/core/tables.py +13 -6
- nautobot/core/templates/40x.html +1 -1
- nautobot/core/templates/500.html +2 -2
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/admin/config/config.html +12 -12
- nautobot/core/templates/admin/index.html +3 -3
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/buttons/export.html +1 -1
- nautobot/core/templates/components/button/dropdown.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/panel/panel.html +3 -3
- nautobot/core/templates/components/tab/content_wrapper.html +6 -7
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
- nautobot/core/templates/echarts/echarts.html +22 -9
- nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
- nautobot/core/templates/generic/object_bulk_create.html +6 -5
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
- nautobot/core/templates/generic/object_bulk_edit.html +1 -1
- nautobot/core/templates/generic/object_bulk_import.html +1 -1
- nautobot/core/templates/generic/object_bulk_remove.html +2 -2
- nautobot/core/templates/generic/object_bulk_update.html +5 -4
- nautobot/core/templates/generic/object_create.html +5 -4
- nautobot/core/templates/generic/object_delete.html +1 -1
- nautobot/core/templates/generic/object_detail.html +1 -1
- nautobot/core/templates/generic/object_edit.html +1 -1
- nautobot/core/templates/generic/object_import.html +2 -1
- nautobot/core/templates/generic/object_list.html +12 -4
- nautobot/core/templates/generic/object_notes.html +5 -3
- nautobot/core/templates/generic/object_retrieve.html +4 -5
- nautobot/core/templates/graphene/graphiql.html +7 -8
- nautobot/core/templates/home.html +1 -1
- nautobot/core/templates/import_success.html +2 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/created_updated.html +7 -3
- nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +11 -1
- nautobot/core/templates/inc/image_attachments.html +2 -1
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +3 -9
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/inc/search_panel.html +4 -4
- nautobot/core/templates/login.html +4 -2
- nautobot/core/templates/nautobot_config.py.j2 +6 -11
- nautobot/core/templates/redoc_ui.html +7 -0
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/search.html +1 -1
- nautobot/core/templates/swagger_ui.html +17 -3
- nautobot/core/templates/system_jobs/import_objects.html +1 -2
- nautobot/core/templates/utilities/confirmation_form.html +2 -2
- nautobot/core/templates/utilities/obj_table.html +10 -2
- nautobot/core/templates/utilities/render_field.html +7 -7
- nautobot/core/templates/utilities/render_jinja2.html +2 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
- nautobot/core/templates/utilities/theme_preview.html +19 -3
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templatetags/helpers.py +76 -18
- nautobot/core/testing/api.py +68 -9
- nautobot/core/testing/filters.py +0 -23
- nautobot/core/testing/integration.py +41 -17
- nautobot/core/testing/mixins.py +2 -0
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -13
- nautobot/core/tests/integration/test_app_home.py +34 -30
- nautobot/core/tests/integration/test_app_navbar.py +3 -0
- nautobot/core/tests/integration/test_filters.py +48 -11
- nautobot/core/tests/integration/test_theme.py +22 -21
- nautobot/core/tests/nautobot_config.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +8 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_breadcrumbs.py +27 -28
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_cli.py +40 -0
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_forms.py +55 -13
- nautobot/core/tests/test_jobs.py +144 -3
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +76 -1
- nautobot/core/tests/test_patch_social_django.py +42 -0
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +62 -13
- nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_tree_queries.py +14 -1
- nautobot/core/tests/test_ui.py +123 -4
- nautobot/core/tests/test_utils.py +72 -5
- nautobot/core/tests/test_views.py +159 -31
- nautobot/core/ui/breadcrumbs.py +70 -29
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +143 -27
- nautobot/core/ui/constants.py +76 -12
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +143 -55
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +20 -9
- nautobot/core/utils/cache.py +2 -1
- nautobot/core/utils/filtering.py +28 -18
- nautobot/core/utils/lookup.py +49 -8
- nautobot/core/utils/module_loading.py +21 -0
- nautobot/core/utils/patch_social_django.py +128 -0
- nautobot/core/views/__init__.py +38 -1
- nautobot/core/views/generic.py +3 -3
- nautobot/core/views/mixins.py +45 -22
- nautobot/core/views/renderers.py +4 -3
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/apps.py +1 -5
- nautobot/data_validation/custom_validators.py +4 -4
- nautobot/data_validation/filters.py +1 -1
- nautobot/data_validation/forms.py +40 -0
- nautobot/data_validation/migrations/0001_initial.py +0 -7
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
- nautobot/data_validation/models.py +16 -7
- nautobot/data_validation/navigation.py +8 -1
- nautobot/data_validation/tables.py +12 -5
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +2 -2
- nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
- nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
- nautobot/data_validation/tests/test_filters.py +8 -6
- nautobot/data_validation/tests/test_models.py +15 -0
- nautobot/data_validation/tests/test_views.py +190 -32
- nautobot/data_validation/urls.py +2 -5
- nautobot/data_validation/views.py +73 -40
- nautobot/dcim/api/serializers.py +3 -13
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +65 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
- nautobot/dcim/forms.py +90 -4
- nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
- nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
- nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
- nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
- nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
- nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
- nautobot/dcim/models/device_component_templates.py +33 -1
- nautobot/dcim/models/device_components.py +98 -64
- nautobot/dcim/models/devices.py +30 -20
- nautobot/dcim/navigation.py +7 -6
- nautobot/dcim/tables/devices.py +18 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +15 -15
- nautobot/dcim/templates/dcim/cable_connect.html +28 -112
- nautobot/dcim/templates/dcim/cable_trace.html +0 -4
- nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
- nautobot/dcim/templates/dcim/consoleport.html +7 -6
- nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
- nautobot/dcim/templates/dcim/device/config.html +2 -2
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
- nautobot/dcim/templates/dcim/device/status.html +8 -8
- nautobot/dcim/templates/dcim/device.html +1 -1
- nautobot/dcim/templates/dcim/device_component_add.html +2 -2
- nautobot/dcim/templates/dcim/device_create.html +5 -3
- nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
- nautobot/dcim/templates/dcim/device_list.html +73 -10
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
- nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
- nautobot/dcim/templates/dcim/frontport.html +10 -9
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
- nautobot/dcim/templates/dcim/interface.html +35 -7
- nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
- nautobot/dcim/templates/dcim/module/base.html +49 -9
- nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/module_frontports.html +1 -1
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_list.html +57 -8
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/module_powerports.html +1 -1
- nautobot/dcim/templates/dcim/module_rearports.html +1 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
- nautobot/dcim/templates/dcim/platform_create.html +1 -1
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +6 -5
- nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
- nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
- nautobot/dcim/templates/dcim/rearport.html +8 -7
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
- nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
- nautobot/dcim/tests/integration/test_controller.py +4 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
- nautobot/dcim/tests/integration/test_create_device.py +0 -2
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
- nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
- nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
- nautobot/dcim/tests/test_api.py +194 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +55 -7
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +328 -4
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +132 -29
- nautobot/dcim/urls.py +64 -21
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +777 -397
- nautobot/extras/api/views.py +60 -45
- nautobot/extras/choices.py +2 -13
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
- nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
- nautobot/extras/{filters/__init__.py → filters.py} +33 -48
- nautobot/extras/forms/forms.py +14 -15
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +11 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/__init__.py +1 -2
- nautobot/extras/models/approvals.py +33 -14
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/groups.py +44 -5
- nautobot/extras/models/jobs.py +60 -4
- nautobot/extras/models/mixins.py +28 -0
- nautobot/extras/models/models.py +23 -2
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/navigation.py +13 -9
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/tables.py +3 -3
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/views.py +0 -9
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/signals.py +20 -19
- nautobot/extras/tables.py +64 -68
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/approval_dashboard.html +7 -5
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +0 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
- nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
- nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
- nautobot/extras/templates/extras/inc/json_format.html +10 -3
- nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
- nautobot/extras/templates/extras/job.html +29 -12
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
- nautobot/extras/templates/extras/job_edit.html +52 -46
- nautobot/extras/templates/extras/job_list.html +29 -25
- nautobot/extras/templates/extras/marketplace.html +5 -9
- nautobot/extras/templates/extras/object_configcontext.html +1 -1
- nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
- nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
- nautobot/extras/templates/extras/plugin_detail.html +29 -24
- nautobot/extras/templates/extras/plugins_list.html +16 -26
- nautobot/extras/templates/extras/role_retrieve.html +64 -0
- nautobot/extras/templates/extras/scheduledjob.html +4 -2
- nautobot/extras/templates/extras/secret_create.html +1 -1
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_computedfields.py +8 -9
- nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
- nautobot/extras/tests/integration/test_customfields.py +9 -10
- nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +18 -6
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +90 -18
- nautobot/extras/tests/test_approvals.py +38 -38
- nautobot/extras/tests/test_changelog.py +59 -5
- nautobot/extras/tests/test_customfields.py +22 -13
- nautobot/extras/tests/test_customfields_filters.py +479 -0
- nautobot/extras/tests/test_dynamicgroups.py +39 -1
- nautobot/extras/tests/test_filters.py +57 -22
- nautobot/extras/tests/test_forms.py +18 -21
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +1 -0
- nautobot/extras/tests/test_models.py +51 -33
- nautobot/extras/tests/test_plugins.py +36 -10
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +52 -112
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +164 -71
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +53 -38
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -2
- nautobot/ipam/signals.py +71 -0
- nautobot/ipam/tables.py +19 -6
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
- nautobot/ipam/templates/ipam/ipaddress.html +14 -0
- nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +14 -13
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +13 -6
- nautobot/ipam/tests/test_filters.py +36 -1
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +44 -2
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +13 -14
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +53 -11
- nautobot/load_balancers/__init__.py +0 -0
- nautobot/load_balancers/api/__init__.py +1 -0
- nautobot/load_balancers/api/serializers.py +75 -0
- nautobot/load_balancers/api/urls.py +23 -0
- nautobot/load_balancers/api/views.py +61 -0
- nautobot/load_balancers/apps.py +17 -0
- nautobot/load_balancers/choices.py +167 -0
- nautobot/load_balancers/filters.py +225 -0
- nautobot/load_balancers/forms.py +532 -0
- nautobot/load_balancers/management/commands/__init__.py +0 -0
- nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
- nautobot/load_balancers/migrations/0001_initial.py +465 -0
- nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
- nautobot/load_balancers/migrations/__init__.py +0 -0
- nautobot/load_balancers/models.py +423 -0
- nautobot/load_balancers/navigation.py +80 -0
- nautobot/load_balancers/tables.py +255 -0
- nautobot/load_balancers/tests/__init__.py +474 -0
- nautobot/load_balancers/tests/test_api.py +353 -0
- nautobot/load_balancers/tests/test_filters.py +134 -0
- nautobot/load_balancers/tests/test_forms.py +266 -0
- nautobot/load_balancers/tests/test_models.py +195 -0
- nautobot/load_balancers/tests/test_views.py +229 -0
- nautobot/load_balancers/urls.py +17 -0
- nautobot/load_balancers/views.py +248 -0
- nautobot/project-static/dist/css/github-dark.min.css +10 -0
- nautobot/project-static/dist/css/github.min.css +10 -0
- nautobot/project-static/dist/css/nautobot.css +1 -11
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/forms.js +13 -0
- nautobot/project-static/js/interface_filtering.js +20 -16
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +1 -1
- nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
- nautobot/project-static/nautobot-icons/elements.svg +1 -1
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +1 -1
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +1 -1
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +1 -1
- nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
- nautobot/project-static/nautobot-icons/star.svg +1 -1
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/collapse.js +3 -3
- nautobot/ui/src/js/nautobot.js +16 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/colors.scss +1 -1
- nautobot/ui/src/scss/nautobot.scss +112 -30
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/users/templates/users/profile.html +45 -12
- nautobot/users/templates/users/sessionkey_delete.html +1 -1
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/views.py +4 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/models.py +1 -68
- nautobot/virtualization/navigation.py +3 -2
- nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +45 -4
- nautobot/virtualization/views.py +4 -1
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +219 -0
- nautobot/vpn/filters.py +234 -0
- nautobot/vpn/forms.py +487 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +535 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +383 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +336 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +293 -0
- nautobot/vpn/tests/test_models.py +147 -0
- nautobot/vpn/tests/test_views.py +300 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +495 -0
- nautobot/wireless/navigation.py +3 -2
- nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
- nautobot/circuits/templates/circuits/circuit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuit_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittermination.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittype.html +0 -2
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
- nautobot/circuits/templates/circuits/provider.html +0 -2
- nautobot/circuits/templates/circuits/provider_edit.html +0 -2
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
- nautobot/circuits/templates/circuits/provider_update.html +0 -1
- nautobot/circuits/templates/circuits/providernetwork.html +0 -2
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
- nautobot/core/templates/buttons/import.html +0 -9
- nautobot/data_validation/template_content.py +0 -42
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/dcim/templates/dcim/controller/base.html +0 -2
- nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/device/base.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
- nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
- nautobot/dcim/templates/dcim/device/frontports.html +0 -2
- nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
- nautobot/dcim/templates/dcim/device/inventory.html +0 -2
- nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
- nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
- nautobot/dcim/templates/dcim/device/powerports.html +0 -2
- nautobot/dcim/templates/dcim/device/rearports.html +0 -2
- nautobot/dcim/templates/dcim/device/wireless.html +0 -2
- nautobot/dcim/templates/dcim/device_component.html +0 -2
- nautobot/dcim/templates/dcim/device_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/devicetype.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/location.html +0 -2
- nautobot/dcim/templates/dcim/location_edit.html +0 -2
- nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
- nautobot/dcim/templates/dcim/locationtype.html +0 -2
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/manufacturer.html +0 -2
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/platform.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/rack.html +0 -2
- nautobot/dcim/templates/dcim/rack_edit.html +0 -2
- nautobot/dcim/templates/dcim/rackgroup.html +0 -2
- nautobot/dcim/templates/dcim/rackreservation.html +0 -2
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
- nautobot/dcim/ui.py +0 -29
- nautobot/extras/templates/extras/computedfield.html +0 -2
- nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontext.html +0 -2
- nautobot/extras/templates/extras/configcontext_edit.html +0 -2
- nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontextschema.html +0 -2
- nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
- nautobot/extras/templates/extras/contact_retrieve.html +0 -2
- nautobot/extras/templates/extras/customfield.html +0 -2
- nautobot/extras/templates/extras/customfield_edit.html +0 -2
- nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/customlink.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
- nautobot/extras/templates/extras/exporttemplate.html +0 -2
- nautobot/extras/templates/extras/gitrepository.html +0 -2
- nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
- nautobot/extras/templates/extras/graphqlquery.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
- nautobot/extras/templates/extras/job_detail.html +0 -2
- nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobhook.html +0 -2
- nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobresult.html +0 -2
- nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
- nautobot/extras/templates/extras/note.html +0 -2
- nautobot/extras/templates/extras/note_retrieve.html +0 -1
- nautobot/extras/templates/extras/object_changelog.html +0 -2
- nautobot/extras/templates/extras/object_notes.html +0 -2
- nautobot/extras/templates/extras/objectchange.html +0 -2
- nautobot/extras/templates/extras/objectchange_list.html +0 -3
- nautobot/extras/templates/extras/relationship.html +0 -1
- nautobot/extras/templates/extras/secret.html +0 -1
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
- nautobot/extras/templates/extras/status.html +0 -2
- nautobot/extras/templates/extras/tag.html +0 -2
- nautobot/extras/templates/extras/tag_edit.html +0 -2
- nautobot/extras/templates/extras/tag_retrieve.html +0 -2
- nautobot/extras/templates/extras/team_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/prefix.html +0 -2
- nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/rir.html +0 -2
- nautobot/ipam/templates/ipam/routetarget.html +0 -1
- nautobot/ipam/templates/ipam/service.html +0 -2
- nautobot/ipam/templates/ipam/service_edit.html +0 -2
- nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlan.html +0 -2
- nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
- nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlangroup.html +0 -2
- nautobot/ipam/templates/ipam/vrf.html +0 -1
- nautobot/tenancy/templates/tenancy/tenant.html +0 -2
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
- /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
nautobot/extras/models/mixins.py
CHANGED
|
@@ -251,3 +251,31 @@ class SavedViewMixin(models.Model):
|
|
|
251
251
|
abstract = True
|
|
252
252
|
|
|
253
253
|
is_saved_view_model = True
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class DataComplianceModelMixin:
|
|
257
|
+
"""
|
|
258
|
+
Adds a `get_data_compliance_url` that can be applied to instances.
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
is_data_compliance_model = True
|
|
262
|
+
|
|
263
|
+
def get_data_compliance_url(self, api=False):
|
|
264
|
+
"""Return the data compliance URL for a given instance."""
|
|
265
|
+
# If is_data_compliance_model overridden should allow to opt out
|
|
266
|
+
if not self.is_data_compliance_model:
|
|
267
|
+
return None
|
|
268
|
+
route = get_route_for_model(self, "data-compliance", api=api)
|
|
269
|
+
|
|
270
|
+
# Iterate the pk-like fields and try to get a URL, or return None.
|
|
271
|
+
fields = ["pk", "slug"]
|
|
272
|
+
for field in fields:
|
|
273
|
+
if not hasattr(self, field):
|
|
274
|
+
continue
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
return reverse(route, kwargs={field: getattr(self, field)})
|
|
278
|
+
except NoReverseMatch:
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
return None
|
nautobot/extras/models/models.py
CHANGED
|
@@ -31,7 +31,13 @@ from nautobot.extras.choices import (
|
|
|
31
31
|
)
|
|
32
32
|
from nautobot.extras.constants import HTTP_CONTENT_TYPE_JSON
|
|
33
33
|
from nautobot.extras.models import ChangeLoggedModel
|
|
34
|
-
from nautobot.extras.models.mixins import
|
|
34
|
+
from nautobot.extras.models.mixins import (
|
|
35
|
+
ContactMixin,
|
|
36
|
+
DataComplianceModelMixin,
|
|
37
|
+
DynamicGroupsModelMixin,
|
|
38
|
+
NotesMixin,
|
|
39
|
+
SavedViewMixin,
|
|
40
|
+
)
|
|
35
41
|
from nautobot.extras.models.relationships import RelationshipModel
|
|
36
42
|
from nautobot.extras.querysets import ConfigContextQuerySet, NotesQuerySet
|
|
37
43
|
from nautobot.extras.utils import extras_features, FeatureQuery, image_upload
|
|
@@ -127,6 +133,7 @@ class ConfigContext(
|
|
|
127
133
|
tenant_groups = models.ManyToManyField(to="tenancy.TenantGroup", related_name="+", blank=True)
|
|
128
134
|
tenants = models.ManyToManyField(to="tenancy.Tenant", related_name="+", blank=True)
|
|
129
135
|
tags = models.ManyToManyField(to="extras.Tag", related_name="+", blank=True)
|
|
136
|
+
device_families = models.ManyToManyField("dcim.DeviceFamily", related_name="+", blank=True)
|
|
130
137
|
|
|
131
138
|
# Due to feature flag CONFIG_CONTEXT_DYNAMIC_GROUPS_ENABLED this field will remain empty unless set to True.
|
|
132
139
|
dynamic_groups = models.ManyToManyField(
|
|
@@ -353,6 +360,14 @@ class CustomLink(
|
|
|
353
360
|
)
|
|
354
361
|
new_window = models.BooleanField(help_text="Force link to open in a new window")
|
|
355
362
|
|
|
363
|
+
is_data_compliance_model = False
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def button_class_css_class(self):
|
|
367
|
+
if self.button_class == ButtonClassChoices.CLASS_DEFAULT:
|
|
368
|
+
return "secondary"
|
|
369
|
+
return self.button_class
|
|
370
|
+
|
|
356
371
|
class Meta:
|
|
357
372
|
ordering = ["group_name", "weight", "name"]
|
|
358
373
|
|
|
@@ -577,6 +592,7 @@ class FileAttachment(BaseModel):
|
|
|
577
592
|
mimetype = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
|
|
578
593
|
|
|
579
594
|
is_metadata_associable_model = False
|
|
595
|
+
is_data_compliance_model = False
|
|
580
596
|
|
|
581
597
|
natural_key_field_names = ["pk"]
|
|
582
598
|
|
|
@@ -638,6 +654,8 @@ class FileProxy(BaseModel):
|
|
|
638
654
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
|
639
655
|
job_result = models.ForeignKey(to=JobResult, null=True, blank=True, on_delete=models.CASCADE, related_name="files")
|
|
640
656
|
|
|
657
|
+
is_data_compliance_model = False
|
|
658
|
+
|
|
641
659
|
def __str__(self):
|
|
642
660
|
return self.name
|
|
643
661
|
|
|
@@ -676,6 +694,7 @@ class FileProxy(BaseModel):
|
|
|
676
694
|
class GraphQLQuery(
|
|
677
695
|
ChangeLoggedModel,
|
|
678
696
|
ContactMixin,
|
|
697
|
+
DataComplianceModelMixin,
|
|
679
698
|
DynamicGroupsModelMixin,
|
|
680
699
|
NotesMixin,
|
|
681
700
|
SavedViewMixin,
|
|
@@ -816,7 +835,7 @@ class ImageAttachment(BaseModel):
|
|
|
816
835
|
|
|
817
836
|
|
|
818
837
|
@extras_features("graphql", "webhooks")
|
|
819
|
-
class Note(ChangeLoggedModel, BaseModel):
|
|
838
|
+
class Note(ChangeLoggedModel, DataComplianceModelMixin, BaseModel):
|
|
820
839
|
"""
|
|
821
840
|
Notes allow anyone with proper permissions to add a note to an object.
|
|
822
841
|
"""
|
|
@@ -877,6 +896,7 @@ class SavedView(BaseModel, ChangeLoggedModel):
|
|
|
877
896
|
is_shared = models.BooleanField(default=True)
|
|
878
897
|
|
|
879
898
|
documentation_static_path = "docs/user-guide/platform-functionality/savedview.html"
|
|
899
|
+
is_data_compliance_model = False
|
|
880
900
|
|
|
881
901
|
class Meta:
|
|
882
902
|
ordering = ["owner", "view", "name"]
|
|
@@ -905,6 +925,7 @@ class UserSavedViewAssociation(BaseModel):
|
|
|
905
925
|
user = models.ForeignKey("users.User", on_delete=models.CASCADE, related_name="saved_view_assignments")
|
|
906
926
|
view_name = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
|
|
907
927
|
is_metadata_associable_model = False
|
|
928
|
+
is_data_compliance_model = False
|
|
908
929
|
|
|
909
930
|
class Meta:
|
|
910
931
|
unique_together = [["user", "view_name"]]
|
|
@@ -141,6 +141,7 @@ class SecretsGroupAssociation(BaseModel):
|
|
|
141
141
|
natural_key_field_names = ["secrets_group", "access_type", "secret_type", "secret"]
|
|
142
142
|
|
|
143
143
|
documentation_static_path = "docs/user-guide/platform-functionality/secret.html"
|
|
144
|
+
is_data_compliance_model = False
|
|
144
145
|
|
|
145
146
|
class Meta:
|
|
146
147
|
unique_together = (
|
|
@@ -7,7 +7,6 @@ from django.utils.hashable import make_hashable
|
|
|
7
7
|
|
|
8
8
|
from nautobot.core.models.fields import ForeignKeyLimitedByContentTypes
|
|
9
9
|
from nautobot.core.models.name_color_content_types import NameColorContentTypesModel
|
|
10
|
-
from nautobot.core.utils.deprecation import class_deprecated
|
|
11
10
|
from nautobot.extras.utils import extras_features, FeatureQuery
|
|
12
11
|
|
|
13
12
|
|
|
@@ -98,17 +97,3 @@ class StatusField(ForeignKeyLimitedByContentTypes):
|
|
|
98
97
|
f"get_{self.name}_color",
|
|
99
98
|
partialmethod(_get_FIELD_color, field=self),
|
|
100
99
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@class_deprecated(message="please directly declare `status = StatusField(...)` on your model instead")
|
|
104
|
-
class StatusModel(models.Model):
|
|
105
|
-
"""
|
|
106
|
-
Deprecated abstract base class for any model which may have statuses.
|
|
107
|
-
|
|
108
|
-
Just directly include a StatusField instead for any new models.
|
|
109
|
-
"""
|
|
110
|
-
|
|
111
|
-
status = StatusField(null=True) # for backward compatibility
|
|
112
|
-
|
|
113
|
-
class Meta:
|
|
114
|
-
abstract = True
|
nautobot/extras/navigation.py
CHANGED
|
@@ -4,11 +4,13 @@ from nautobot.core.apps import (
|
|
|
4
4
|
NavMenuItem,
|
|
5
5
|
NavMenuTab,
|
|
6
6
|
)
|
|
7
|
+
from nautobot.core.ui.choices import NavigationIconChoices, NavigationWeightChoices
|
|
7
8
|
|
|
8
9
|
menu_items = (
|
|
9
10
|
NavMenuTab(
|
|
10
11
|
name="Approvals",
|
|
11
|
-
icon=
|
|
12
|
+
icon=NavigationIconChoices.APPROVAL_WORKFLOWS,
|
|
13
|
+
weight=NavigationWeightChoices.APPROVAL_WORKFLOWS,
|
|
12
14
|
groups=(
|
|
13
15
|
NavMenuGroup(
|
|
14
16
|
name="Approval Workflows",
|
|
@@ -38,7 +40,8 @@ menu_items = (
|
|
|
38
40
|
),
|
|
39
41
|
NavMenuTab(
|
|
40
42
|
name="Organization",
|
|
41
|
-
|
|
43
|
+
icon=NavigationIconChoices.ORGANIZATION,
|
|
44
|
+
weight=NavigationWeightChoices.ORGANIZATION,
|
|
42
45
|
groups=(
|
|
43
46
|
NavMenuGroup(
|
|
44
47
|
name="Contacts",
|
|
@@ -140,8 +143,8 @@ menu_items = (
|
|
|
140
143
|
),
|
|
141
144
|
NavMenuTab(
|
|
142
145
|
name="Secrets",
|
|
143
|
-
icon=
|
|
144
|
-
weight=
|
|
146
|
+
icon=NavigationIconChoices.SECRETS,
|
|
147
|
+
weight=NavigationWeightChoices.SECRETS,
|
|
145
148
|
groups=(
|
|
146
149
|
NavMenuGroup(
|
|
147
150
|
name="Secrets",
|
|
@@ -169,8 +172,8 @@ menu_items = (
|
|
|
169
172
|
),
|
|
170
173
|
NavMenuTab(
|
|
171
174
|
name="Jobs",
|
|
172
|
-
icon=
|
|
173
|
-
weight=
|
|
175
|
+
icon=NavigationIconChoices.JOBS,
|
|
176
|
+
weight=NavigationWeightChoices.JOBS,
|
|
174
177
|
groups=(
|
|
175
178
|
NavMenuGroup(
|
|
176
179
|
name="Jobs",
|
|
@@ -258,7 +261,8 @@ menu_items = (
|
|
|
258
261
|
),
|
|
259
262
|
NavMenuTab(
|
|
260
263
|
name="Extensibility",
|
|
261
|
-
|
|
264
|
+
icon=NavigationIconChoices.EXTENSIBILITY,
|
|
265
|
+
weight=NavigationWeightChoices.EXTENSIBILITY,
|
|
262
266
|
groups=(
|
|
263
267
|
NavMenuGroup(
|
|
264
268
|
name="Logging",
|
|
@@ -532,8 +536,8 @@ menu_items = (
|
|
|
532
536
|
),
|
|
533
537
|
NavMenuTab(
|
|
534
538
|
name="Apps",
|
|
535
|
-
icon=
|
|
536
|
-
weight=
|
|
539
|
+
icon=NavigationIconChoices.APPS,
|
|
540
|
+
weight=NavigationWeightChoices.APPS,
|
|
537
541
|
groups=(
|
|
538
542
|
NavMenuGroup(
|
|
539
543
|
name="General",
|
|
@@ -5,9 +5,10 @@ import inspect
|
|
|
5
5
|
from logging import getLogger
|
|
6
6
|
|
|
7
7
|
from django.conf import settings
|
|
8
|
+
from django.conf.urls import include
|
|
8
9
|
from django.core.exceptions import ValidationError
|
|
9
10
|
from django.template.loader import get_template
|
|
10
|
-
from django.urls import get_resolver, URLPattern
|
|
11
|
+
from django.urls import clear_url_caches, get_resolver, path, URLPattern
|
|
11
12
|
from packaging import version
|
|
12
13
|
|
|
13
14
|
from nautobot.core.apps import (
|
|
@@ -17,10 +18,9 @@ from nautobot.core.apps import (
|
|
|
17
18
|
register_menu_items,
|
|
18
19
|
)
|
|
19
20
|
from nautobot.core.signals import nautobot_database_ready
|
|
20
|
-
from nautobot.core.utils.
|
|
21
|
+
from nautobot.core.utils.module_loading import import_string_optional
|
|
21
22
|
from nautobot.extras.choices import BannerClassChoices
|
|
22
23
|
from nautobot.extras.plugins.exceptions import PluginImproperlyConfigured
|
|
23
|
-
from nautobot.extras.plugins.utils import import_object
|
|
24
24
|
from nautobot.extras.registry import register_datasource_contents, registry
|
|
25
25
|
from nautobot.extras.secrets import register_secrets_provider
|
|
26
26
|
|
|
@@ -105,9 +105,24 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
105
105
|
"""Callback after plugin app is loaded."""
|
|
106
106
|
# We don't call super().ready here because we don't need or use the on-ready behavior of a core Nautobot app
|
|
107
107
|
|
|
108
|
+
from nautobot.extras.plugins.urls import BASE_URL_TO_APP_LABEL, plugin_api_patterns, plugin_patterns
|
|
109
|
+
|
|
108
110
|
# Introspect URL patterns and models to make available to the installed-plugins detail UI view.
|
|
109
|
-
urlpatterns =
|
|
110
|
-
api_urlpatterns =
|
|
111
|
+
urlpatterns = import_string_optional(f"{self.__module__}.urls.urlpatterns")
|
|
112
|
+
api_urlpatterns = import_string_optional(f"{self.__module__}.api.urls.urlpatterns")
|
|
113
|
+
|
|
114
|
+
base_url = self.base_url or self.label
|
|
115
|
+
|
|
116
|
+
if urlpatterns is not None:
|
|
117
|
+
plugin_patterns.append(path(f"{base_url}/", include((urlpatterns, self.label))))
|
|
118
|
+
|
|
119
|
+
if api_urlpatterns is not None:
|
|
120
|
+
plugin_api_patterns.append(path(f"{base_url}/", include((api_urlpatterns, f"{self.label}-api"))))
|
|
121
|
+
|
|
122
|
+
if any([urlpatterns, api_urlpatterns]):
|
|
123
|
+
clear_url_caches()
|
|
124
|
+
|
|
125
|
+
BASE_URL_TO_APP_LABEL[base_url] = self.label
|
|
111
126
|
|
|
112
127
|
self.features = {
|
|
113
128
|
"api_urlpatterns": sorted(
|
|
@@ -123,36 +138,31 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
123
138
|
}
|
|
124
139
|
|
|
125
140
|
# Register banner function (if defined)
|
|
126
|
-
banner_function
|
|
127
|
-
if banner_function is not None:
|
|
141
|
+
if banner_function := import_string_optional(f"{self.__module__}.{self.banner_function}"):
|
|
128
142
|
register_banner_function(banner_function)
|
|
129
143
|
self.features["banner"] = True
|
|
130
144
|
|
|
131
145
|
# Register model validators (if defined)
|
|
132
|
-
validators
|
|
133
|
-
if validators is not None:
|
|
146
|
+
if validators := import_string_optional(f"{self.__module__}.{self.custom_validators}"):
|
|
134
147
|
register_custom_validators(validators)
|
|
135
148
|
self.features["custom_validators"] = sorted(set(validator.model for validator in validators))
|
|
136
149
|
|
|
137
150
|
# Register datasource contents (if defined)
|
|
138
|
-
datasource_contents
|
|
139
|
-
if datasource_contents is not None:
|
|
151
|
+
if datasource_contents := import_string_optional(f"{self.__module__}.{self.datasource_contents}"):
|
|
140
152
|
register_datasource_contents(datasource_contents)
|
|
141
153
|
self.features["datasource_contents"] = datasource_contents
|
|
142
154
|
|
|
143
155
|
# Register GraphQL types (if defined)
|
|
144
|
-
graphql_types
|
|
145
|
-
if graphql_types is not None:
|
|
156
|
+
if graphql_types := import_string_optional(f"{self.__module__}.{self.graphql_types}"):
|
|
146
157
|
register_graphql_types(graphql_types)
|
|
147
158
|
|
|
148
159
|
# Import jobs (if present)
|
|
149
160
|
# Note that we do *not* auto-call `register_jobs()` - the App is responsible for doing so when imported.
|
|
150
|
-
jobs
|
|
151
|
-
if jobs is not None:
|
|
161
|
+
if jobs := import_string_optional(f"{self.__module__}.{self.jobs}"):
|
|
152
162
|
self.features["jobs"] = jobs
|
|
153
163
|
|
|
154
164
|
# Import metrics (if present)
|
|
155
|
-
metrics =
|
|
165
|
+
metrics = import_string_optional(f"{self.__module__}.{self.metrics}")
|
|
156
166
|
if metrics is not None and self.name not in settings.METRICS_DISABLED_APPS:
|
|
157
167
|
register_metrics(metrics)
|
|
158
168
|
self.features["metrics"] = [] # Initialize as empty, to be filled by the signal handler
|
|
@@ -161,19 +171,16 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
161
171
|
nautobot_database_ready.connect(signal_callback, sender=self)
|
|
162
172
|
|
|
163
173
|
# Register plugin navigation menu items (if defined)
|
|
164
|
-
menu_items
|
|
165
|
-
if menu_items is not None:
|
|
174
|
+
if menu_items := import_string_optional(f"{self.__module__}.{self.menu_items}"):
|
|
166
175
|
register_plugin_menu_items(self.verbose_name, menu_items)
|
|
167
176
|
self.features["nav_menu"] = menu_items
|
|
168
177
|
|
|
169
|
-
homepage_layout
|
|
170
|
-
if homepage_layout is not None:
|
|
178
|
+
if homepage_layout := import_string_optional(f"{self.__module__}.{self.homepage_layout}"):
|
|
171
179
|
register_homepage_panels(self.path, self.label, homepage_layout)
|
|
172
180
|
self.features["home_page"] = homepage_layout
|
|
173
181
|
|
|
174
182
|
# Register template content (if defined)
|
|
175
|
-
template_extensions
|
|
176
|
-
if template_extensions is not None:
|
|
183
|
+
if template_extensions := import_string_optional(f"{self.__module__}.{self.template_extensions}"):
|
|
177
184
|
register_template_extensions(template_extensions)
|
|
178
185
|
self.features["template_extensions"] = sorted(set(extension.model for extension in template_extensions))
|
|
179
186
|
|
|
@@ -185,15 +192,13 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
185
192
|
pass
|
|
186
193
|
|
|
187
194
|
# Register secrets providers (if any)
|
|
188
|
-
secrets_providers
|
|
189
|
-
if secrets_providers is not None:
|
|
195
|
+
if secrets_providers := import_string_optional(f"{self.__module__}.{self.secrets_providers}"):
|
|
190
196
|
for secrets_provider in secrets_providers:
|
|
191
197
|
register_secrets_provider(secrets_provider)
|
|
192
198
|
self.features["secrets_providers"] = secrets_providers
|
|
193
199
|
|
|
194
200
|
# Register custom filters (if any)
|
|
195
|
-
filter_extensions
|
|
196
|
-
if filter_extensions is not None:
|
|
201
|
+
if filter_extensions := import_string_optional(f"{self.__module__}.{self.filter_extensions}"):
|
|
197
202
|
register_filter_extensions(filter_extensions, self.name)
|
|
198
203
|
self.features["filter_extensions"] = {"filterset_fields": [], "filterform_fields": []}
|
|
199
204
|
for filter_extension in filter_extensions:
|
|
@@ -207,8 +212,7 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
207
212
|
)
|
|
208
213
|
|
|
209
214
|
# Register override view (if any)
|
|
210
|
-
override_views
|
|
211
|
-
if override_views is not None:
|
|
215
|
+
if override_views := import_string_optional(f"{self.__module__}.{self.override_views}"):
|
|
212
216
|
for qualified_view_name, view in override_views.items():
|
|
213
217
|
view_class_name = view.view_class.__name__ if hasattr(view, "view_class") else view.cls.__name__
|
|
214
218
|
self.features.setdefault("overridden_views", []).append(
|
|
@@ -272,17 +276,11 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
272
276
|
|
|
273
277
|
def _register_table_extensions(self):
|
|
274
278
|
"""Register tables extensions (if any)."""
|
|
275
|
-
table_extensions
|
|
276
|
-
if table_extensions is not None:
|
|
279
|
+
if table_extensions := import_string_optional(f"{self.__module__}.{self.table_extensions}"):
|
|
277
280
|
register_table_extensions(table_extensions, self.name)
|
|
278
281
|
self.features["table_extensions"] = get_table_extension_features(table_extensions)
|
|
279
282
|
|
|
280
283
|
|
|
281
|
-
@class_deprecated_in_favor_of(NautobotAppConfig)
|
|
282
|
-
class PluginConfig(NautobotAppConfig):
|
|
283
|
-
pass
|
|
284
|
-
|
|
285
|
-
|
|
286
284
|
#
|
|
287
285
|
# Template content injection
|
|
288
286
|
#
|
|
@@ -413,11 +411,6 @@ class TemplateExtension:
|
|
|
413
411
|
raise NotImplementedError
|
|
414
412
|
|
|
415
413
|
|
|
416
|
-
@class_deprecated_in_favor_of(TemplateExtension)
|
|
417
|
-
class PluginTemplateExtension(TemplateExtension):
|
|
418
|
-
pass
|
|
419
|
-
|
|
420
|
-
|
|
421
414
|
def register_template_extensions(class_list):
|
|
422
415
|
"""
|
|
423
416
|
Register a list of TemplateExtension classes
|
|
@@ -444,11 +437,6 @@ class Banner:
|
|
|
444
437
|
self.banner_class = banner_class
|
|
445
438
|
|
|
446
439
|
|
|
447
|
-
@class_deprecated_in_favor_of(Banner)
|
|
448
|
-
class PluginBanner(Banner):
|
|
449
|
-
pass
|
|
450
|
-
|
|
451
|
-
|
|
452
440
|
def register_banner_function(function):
|
|
453
441
|
"""
|
|
454
442
|
Register a function that may return a Banner object.
|
|
@@ -494,11 +482,6 @@ class FilterExtension:
|
|
|
494
482
|
filterform_fields = {}
|
|
495
483
|
|
|
496
484
|
|
|
497
|
-
@class_deprecated_in_favor_of(FilterExtension)
|
|
498
|
-
class PluginFilterExtension(FilterExtension):
|
|
499
|
-
pass
|
|
500
|
-
|
|
501
|
-
|
|
502
485
|
def register_filter_extensions(filter_extensions, plugin_name):
|
|
503
486
|
"""
|
|
504
487
|
Register a list of FilterExtension classes
|
|
@@ -789,11 +772,6 @@ class CustomValidator:
|
|
|
789
772
|
raise NotImplementedError
|
|
790
773
|
|
|
791
774
|
|
|
792
|
-
@class_deprecated_in_favor_of(CustomValidator)
|
|
793
|
-
class PluginCustomValidator(CustomValidator):
|
|
794
|
-
pass
|
|
795
|
-
|
|
796
|
-
|
|
797
775
|
def register_custom_validators(class_list):
|
|
798
776
|
"""
|
|
799
777
|
Register a list of CustomValidator classes
|
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
"apps":
|
|
3
|
+
- "name": "Ansible Automation"
|
|
4
|
+
"use_cases":
|
|
5
|
+
- "Network Automation"
|
|
6
|
+
- "Ansible Playbook Execution"
|
|
7
|
+
"requires": []
|
|
8
|
+
"docs": "https://docs.nautobot.com/projects/ansible-automation/en/stable/"
|
|
9
|
+
"headline": >-
|
|
10
|
+
Execute and monitor automation workflows from Redhat Ansible Automation Platform seamlessly without leaving
|
|
11
|
+
Nautobot.
|
|
12
|
+
"description": >-
|
|
13
|
+
Nautobot Ansible Automation seamlessly integrates automation workflows from Redhat Ansible Automation Platform
|
|
14
|
+
as Nautobot Jobs, allowing its users to run and visualize the output of such workflows directly within Nautobot.
|
|
15
|
+
The Nautobot App connects with Redhat AAP/AWX platforms in order to selectively import Job Templates and Workflows
|
|
16
|
+
so they can be triggered to run from Nautobot. Other features include: historical result synchronization, support
|
|
17
|
+
for input customization using surveys, and deeper integration using Nautobot inventory data.
|
|
18
|
+
"package_name": "nautobot_ansible_automation"
|
|
19
|
+
"author": "Network to Code (NTC)"
|
|
20
|
+
"display":
|
|
21
|
+
"cloud": true
|
|
22
|
+
"oss": true
|
|
23
|
+
"enterprise": true
|
|
24
|
+
"availability": "NTC License"
|
|
25
|
+
"icon": >-
|
|
26
|
+
data:image/png;base64,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
|
|
3
27
|
- "name": "BGP Models"
|
|
4
28
|
"use_cases":
|
|
5
29
|
- "Routing"
|
|
@@ -112,7 +136,6 @@
|
|
|
112
136
|
"availability": "Open Source"
|
|
113
137
|
"icon": >-
|
|
114
138
|
data:image/png;base64,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
|
|
115
|
-
# TODO: remove data_validation_engine from marketplace repo so it doesn't get reintroduced here by accident
|
|
116
139
|
- "name": "Design Builder"
|
|
117
140
|
"use_cases":
|
|
118
141
|
- "Network Design"
|
|
@@ -430,6 +453,31 @@
|
|
|
430
453
|
"availability": "Open Source"
|
|
431
454
|
"icon": >-
|
|
432
455
|
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAOhklEQVR42u2bCVhV1RbHDzM44qxpz6nSnpaVlc/ST9Mces/MqbTB96ynveyZDw0FREBQzJw1c86cQ2S69wKGiKQoOYtTKiaXSXNCEZVB0fP++/I/eO7xknDB6fs43/f7zgXu2Wfvtddea+21NpJUeVVelVflVXk9+Cs8PNxERETEfRHfe+IvMRCdTlfSIO2AE3AhDsDmiRdICTMsBtcU9ARuYAEIBVvAdvALiAZrwBQwFLQHrpaE8UQMXK/Xi3szMByEgFSQD/LAOXAc7AEJYCdIAkaQDW6Cq2A/mAm6gWqPrSDUnYqKihL3tuy4GNBtIIO94L+gK3gG1AXVQRVQFdQET4F2YACYSuEIQeSCreATrVY88qWhmZXGVOE/2OlIMB5cBAtLY/w0CMG8DgJBMjUoHrwL7B+pNqhVHp+F8eoDDlB9xcB7AWcOYp9BFx4np41wqDs5V5J85LtMlEtjQxThLqI25dKONHkkQtB0UqiwN1X1NPiMg5ZiDCGS5ChLG8IMK79dt+3804E5ozDgDzHw9sDRTBB/rll2nPVDtBHbqA27QMeHKgRNx2qBJZwV0Zk2yt8+XHRcmeEWTwVe21rDv0C28ZFv4Xc3QTZYBZqrBVDC7D/H2b4OToH3aRBHgPMgDfRVu9uHJQAx+NU0ckLtzwB/8Dyw9Vn1qyR5yw0xuK3gBlgM+oMBYBnIA7GggRCAPuKeJfUs8KX3yOO7WmuEI7zDCdqYQQ9cEzRqL2b+Fq19fxBDtRQubnWkLmxgvcm533LGx0ATbFUqLz67S0UaMW7ksiQIIMwWzzUE74DFIIOCFUavH4MmSxryKjjK9/ZUPEOFC0EzO2LN3wErGMVJVEuxTn/SoTNBYZEFraZl3cbAd2GQrmbrvYhaYL9rQP6x1SHRPngmCM+epBAvRxRdA0ENS/5fPVDaASO1oe0D0QJVB/rQEIl1v14lAAX7aH1oq1nrfxmNwYm1HgQh2JhZ+4km22ALgv8yNUdeFRJ9AwJIwbN6MBa8BBzvF/hobIbQwmvAwJii4oSgcUUHaIw2gAJGemYdnbxmhySNlKtg8LvBDlC12OXd1YDqILHqpJtHZ6+P7xKlD2vMkNmsLWHcStm3BtQAMTHuFWYPFCkztJ3CdTmM63U/X9rM7GV3B/kNyAdDLSyBYaAATPZauVu4StPzR44cue+gSxBCG3oFmZ6hYpaCSpIvMsKLVPw8+JAC8S9BAC1BEsgCE0EH8DfgC224CPaC5sWaUb7+CeN5g/sOEZMsgCBtyx0uq2Z/JiOwXqqXCsMXSwPUolgI3mYzLYKeLZxt4Q6v2/neKXx5xnnZbcW+adIwWdps2Gj1TKn6Mpb96wHWgQvcV1ivBarGm3GQ0RaM3mBqwVgzaZuveeEF+gAv4NFwyvXBsPzxel34ZVp6q9crn7NhnJBOO9WdwgisKAEMp8//xIIfrsut7E7FZZlZ+3vXvtR5ToaEOOEF+u8/6FmsEgKfqc0weTPjhWoMl4+AelYvA1UyI4wa8BetLyaBDFXfvJ/lFn8PCwtT++8TNF4faTNClq5UTycz9s7tJ4VExiEY0gkXOFn1vDtjit5WawEfbErVEj7fVt2Q6mVvUeU8xc+RkZFl2U90AAfpwycwR2BRCKkezkUUDb46eCN9vDRw1/dDF0ZvWJYXodP3jFk3RwrXmWzWayCHeYWyC0DViZ6U5ChtQ0ZPZ9PLwnWR9XE/gbvubVm2PT65VfEMlVIIrehdCkE4eEW71EzticF7OYrPncEWcB3cNHpVlZP9mhT+7lPPJ/NryWXXgqHKXiWJRtq5PAJw4+x2Undo+1I36dwsSTrl00DaP7t31dg102LiV0xIPxHwzD/Sx9s0VKup0c21NEKoTRXOpl3wU8cXYfpopb03QBo4B6aCgamejl+kejj8IoQBJmV8Ldmu2pZpQ2+QwkyT1QL4jut7OLemxSq6f/Y7okNtwU8pE6rfEDOBz3ngOBgNXMqiCbAdNtzhxdCz/E6hvBISFe90dYTkhLYM4CLoobEH9UA4uIIJ6HDkm9dEm9MZE7xsrQBEIiKY0VUBt51i3z8vXB/V7+CMbl0h/SS8NAf3peAzfP4a/ApugYBUD0c7Uwfdq5bFLgghf8ydoNgKXwnXGX6OX+E9z+hVBe9ymp/q6XyPQSxaGo7XMPgIfH85njvL5dvDWgE4cQ0l0wbMp7vL0oWH5ifN6Jqd5uGQjRd/lO7poO5IfRAKroIu4ndpXg5lTbYoghA26HsM6FjcqoC8lAnVoGWOn6s1K9WrWBhN0jzsjViSmDDdWbrBm0quwBoBuDBvvx3qqezOqoUZfm6LznjD6GDNOa4DdhZm43UKYIZRCMDT0dqskxQSEiIFxeyru2/ue0ONXi65aHNykVGEQfQwe+dL6MuVE/4t1sI1NuPAC+hirRbANs66Ev9Le+YNkjLdpfZC3fDS8erZMJqvyZNgY5qng21ZBKBcDMFN7FgyUjo/WqqB9hJBMmitEbgD+E7YoDQPu97blo8Tz71HAQyxVgAODH8PKntsVTAijN9lMLtoNpy0s9ESnIWQlkUG/ygdnNm9XDuz0+7F7Q5Cm1dh9Xfj8wegDegEltDurBLGd1PQYol2RAigr7UCUGJso9qVsCPVgHA9KeCv6tnI8RbfcZxoxHr9deGwHVDHF8u7PxftZoyTJATTNscC2/mdnNQUtsC5kJNwgzHBD8DkgmED7nHh1rrBKfTN7ZSYOnW8ozLY/kUewEnMxnugOXiB1v/aCf+WyYaQtVnojBDgEDxrW56YP0y/SQqOTkBBRLdiU9CiK8cCXwiAEQ7E+zxpbIWblOQ3ivs/j7vC58ojgKG0pAOKozIvZbadbXH/N0iHSmLt2WdyRgqA/nef+s/DXYrt82Hu1ScyoVq6stZE1Y7STVb6I1zadUSeSyVZtkEEeI87VHmwzYwGa5VZAKp8W3sGE1PVHU+lZT/g/5KUMtG1zd55A/YemNXraop3zXlQ/374e41kv6eVuPxZ5vsKmfGtLVT00pfqzY2jCdPlK2urR3ZSd9nmdNzy6rqiIOmsopEzj8tSOtxghpeTtt/KHmatdg9TVi1wZeorQT17W5DEqDLplvT2nFOSd3BynTCd4aBeFxG7fJ/scMqvsWlQp71dJcQLSjt1WB8UQgjeGJ3Q6PZgSWhQTVBFPYOqbXQTkT8A4fjdum5z01euC43KQx5hYiyTKMqAxQZM4z770gCOLO9uUMkG5XP2PhD5t4Ton6rXDsgTRQ0vZ7/C+G5z0wpH/3BgQbvp56Tuc9OkqA3LLfl0F4a2edg3xGEJrcWARSSZADxAHbhMdTptO+sKJ4HR3ve2jEzSmRFLD7ca9+MeS4GTDb1VGyZtLzGVV24BvMX9gEx7kBUcZtjdelrWLsyMSHoew/0S7qdwbztq+YESK77zD8v2iQs/XZLs11hEdCKm3wz204Wtz/SQakjvmgSwkNWjr0A9asNMtF9g63Pnq7StS4U2Ps3CiMgqefGAxX6G7KKvOqWgUt4iqMiwxLFg4YlK76zp67btremfL6NDK1nkGCRyfp1mZ8ZG6cLGUFO6cK2KfGGjUENMLQRRDWEwt2LwqbTcIoBxFRGjEELahCqf1pwhN0JbySAS7Tuo7EED/PwbCi7nQsL1e7jGczgp15kNjmOBpZBxQPkyw6rZ+ydf5P6vxUelWgF5r6NDOWAujVZndC67/4JTuVijN7j+8lTb2xQYxKOb1846DNtwGwKYqbHeLeDSMg5P75S5eENsIsrooqq0Wppkllusjs97IIBrSKPHY3p/RLs+DHU7Mh/YknuAPbQ7FSYAV4bFqcjntZZGmQofenToKu4R4AS41G76hfc3G0I6Mg4fxZS52FKvwYYmPHbN9IQUb1cEMI4zNAJoDjeacWh657MYXOKz0y5nou1UtNnRJGA/kwAGg1wH39uzk2JWO8lXxlpaZr7MX35aYSdINGUxMbsr5d/c7UYtPzi8y5yMO1Um3TzPCtAQEautDY26p2MGg8Fmxc5s2yPfvFoHxm87BmwEbwJ7eoJpEEpByoQaw2VZtu813+iO0pqMNtNYWV4NroCjEEzrplOvSj8bQrWn0Doy8NmkrilWpADsWasXHsETqr4da9H4n2WHOkjuLH+p6v2ic8qGRnyGBkinfBuJ2f47MzoXQJSIJLHLk0/6N0dE6ewaoTOIYzHHPVfuPuPoV5iA9v4AmSAYvGJqf6Rsqj+o+laf54guKsnZCi2Qql4kjqUk8lxAPtbhKHEYAgbxbuRWUjzPfftvXqYACMkUpyBwBGt/5745fY9Fblx5BkL6kuny8yiy9mFdUbjEpmi76FSJV9F7NO51EVXfTV00rbDLgqqlMkLsWpb4XrPuhfrXyv5cckFCFRsW3QVa7zNK6N1uxgXz80S423nfVvdF5CkCVFGmywM7JGEh0rpAi9uxrEJIA2fHSBJCaClx0XCJA86i/w4oqT1NSdyZ3xUzv5FFmgd7TEa9H6ClP8ftcj9VweNPO6ERpCO9xWUmQc8yAXOPC9M8V59qX8jBN3hoh6U0HenJ6k4OXVDtPzvYoPlbA7rIm3SxImz9H2f0c3oPs2oSl2JHGrxbVPu6pT1P8KCE0JYnMwqZye1t6YSHupNMUmznIH5QEi48d3CIJ0vrWjgr6Muld4EJD5dHdmBSc06nJutxmYwVgqgd1SwUQDzorkRN8CsLFecx1Ioh/Lk5f3eUAtukdnWP9OywhRluy23vRaajtlEwr/IQQxzjiE2s31lq43kKMpEqnsqB72WEV+OxOzStGYAtDzoF0kvk00bk0cqnUgv682RZP25c3JjG2qL6bhaX18faJfHQ1ruVR2gV6tEmTGXJK5mDyuOGSU0u13YSt7Zf0DAWnxEMDQ19/AZeSkEofvsp1up6cB//Edd6XxpGkcCspZzveaxnvJzCKNX/DT2Rg668Kq/Kq/KqvJ6g6/8R34/Jrl4zYgAAAABJRU5ErkJggg==
|
|
456
|
+
- "name": "Version Control"
|
|
457
|
+
"use_cases":
|
|
458
|
+
- "Enterprise Governance"
|
|
459
|
+
- "Branching"
|
|
460
|
+
- "Approvals"
|
|
461
|
+
- "Data Management"
|
|
462
|
+
"requires": []
|
|
463
|
+
"docs": "https://docs.nautobot.com/projects/version-control/en/latest/"
|
|
464
|
+
"headline": >-
|
|
465
|
+
Allows users to have change (workflow) management with approvals when managing data within Nautobot ensuring
|
|
466
|
+
Enterprise can maintain governance of their data.
|
|
467
|
+
"description": >-
|
|
468
|
+
Version Control allows users to stage changes and have them reviewed by others before committing them. It offers
|
|
469
|
+
Git semantics when working with Nautobot using branch, commit, merge, and pull request concepts allowing users
|
|
470
|
+
to stage changes in a branch and have it be later merged after someone approves it in the UI or API. This app
|
|
471
|
+
requires a Dolt database.
|
|
472
|
+
"package_name": "nautobot_version_control"
|
|
473
|
+
"author": "Network to Code (NTC)"
|
|
474
|
+
"display":
|
|
475
|
+
"cloud": true
|
|
476
|
+
"oss": false
|
|
477
|
+
"enterprise": false
|
|
478
|
+
"availability": "NTC License"
|
|
479
|
+
"icon": >-
|
|
480
|
+
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABoVBMVEUAAACvr6+wsLCxsbEAff8AfP8Ae/+BosVYlti1sq61sa7geAbmeADgeAe8oYPIlFvheAbheAiysrKvr6+wsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCvr6+wsLAAfP8AfP8Aff8Aff8Abf+xsbEAff8Aff8Cfv5Xlti7s6oAfP8AfP8kh++orrQAfP8jh++xsLACfv68tKsAef+wsLCwsLCwsLCvr6+xsbGwsLCvsLCxsLCvsLGrr7LCtqgPgfgAdP8Aff8Aff8AfP/CtagAdf8Aff/BtagAff8Aff+rrrL0eADueADfeAfgeAfgeAfuaADgeAbgeAfgeAffeQnIlFqss7vgeAbhdwTWgymyrajhdwSwsLHfeQnJlFqttLzjdQCwsLCwsLCwsLDfeAbheAawsLCwsLCwsLDheQawsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLBnm9FLkjVQAAAAiXRSTlMAAAAAAAAAAAAAAAAAAAAAAAAAAgkCeMLBhg72xfOVDRL8ls+eFggMCIcCCXjCAgL2xcQSCRLC88W/88LFCQmIwHoDA5oNlf6aDJoM+5YNDAz8DAINmgIJAnjCAgn2xcQSCRLC88W/wsUSCQn+Hd0CAk3X+AKM8EGTyAsza/2Qx+/cA6qLTKlqMjF60isAAAL+SURBVFjD7ZfbWxJBGMZlliV1tSgOu8ECghoJ0kpm2ZEsFEMp07IkpDLtqGVaeOhg2XH9q5uZZQ+4s7tDV174Xu3Dw/vb+Wa+eWe2peVQB0YuYCkXFQAAn5+oACUB+II8ScLJEB0B+PmwaPaLETkaoyJAgNhlLqBLkOMJKgIE8H7zBPqF7h46ggIg/Cr0RqkIlgD+VDJBQ7AG+EGMhmAHoCLYAmgIdgCGhmA/AgCSTmthCai31+nenngUdrUDwM02yGNocKFblvsCTgCWTaU1pVjWuMUEISL4HQGp/oym/jNHAJAM+0LknQHpzEBWsWcHzg6egzafvi94KkB2SClg6PyFYTTyoFo3LSCTViYwffHS8GVeDGumpgGZK4NXjXU3XUI2c63VaKIF6JMIx9I8oGEZU/8BaGgkNwngsjgqLDrRBIB+H/GosCtBeaf6HMrlSEeF5STi3QjfCf8hijidZJm0sUnLiADXI3A3iiOwHcGNkZGbKFnyeVI0EBrJCJAgAEgSGIXJlCTGtBVAKUGCJTAw2UbHCuO32nDA7Q8XqxLUSWTQ+sUShb3ihKcVJPNyLgCa60Tsj48XJ0u3Pe0IIIGmOlHxJ+5Mle6WJqYT5nxUG2lGb6QZQyfW/bE29l5psngfTyJDAHAdsw80zXZwKsClnQxudqq4V4ia/HVA58O5sqa5R0f1/q/7AWifHi+Mmf3qCCrl+apir84/fvJUCRSjHz0R3m8AVBcqWAvPFpd4JdL2+RMkvw4oVzisyvPFpRcoVCWGxk8AlF++eq00YYjCTyihWn5zDLsCueV80slPmkQ4FtwBK29lBWDnVwHvjMu46sVX2PfhtXVktPWrgM7ViqYPHIcBH2sbm/omZmxD9biX0+Xl8AhCW1vJ7c31eowwTV40oT7VcmB7Y01ejtr5La66MAvB5y9f+3a+hb/nbP3kyzbKwt0fcry28/PXimTrJ1/3URYGgr///I3VU8n2gyNA+NqQUB/tYvcJh7syikyS0M8u5vCT8KDrH3bO2ltvf7BdAAAAAElFTkSuQmCC
|
|
433
481
|
- "name": "Welcome Wizard"
|
|
434
482
|
"use_cases":
|
|
435
483
|
- "Onboarding"
|
|
@@ -20,21 +20,21 @@ class InstalledAppsTable(tables.Table):
|
|
|
20
20
|
{% if record.home_url %}
|
|
21
21
|
<a href="{% url record.home_url %}" class="btn btn-primary btn-xs" title="Home">
|
|
22
22
|
{% else %}
|
|
23
|
-
<a
|
|
23
|
+
<a class="btn btn-primary btn-xs disabled" aria-disabled="true" title="No home link provided">
|
|
24
24
|
{% endif %}
|
|
25
25
|
<i class="mdi mdi-home"></i>
|
|
26
26
|
</a>
|
|
27
27
|
{% if record.config_url %}
|
|
28
28
|
<a href="{% url record.config_url %}" class="btn btn-warning btn-xs" title="Configure">
|
|
29
29
|
{% else %}
|
|
30
|
-
<a
|
|
30
|
+
<a class="btn btn-warning btn-xs disabled" aria-disabled="true" title="No configuration link provided">
|
|
31
31
|
{% endif %}
|
|
32
32
|
<i class="mdi mdi-cog"></i>
|
|
33
33
|
</a>
|
|
34
34
|
{% if record.docs_url %}
|
|
35
35
|
<a href="{% url record.docs_url %}" class="btn btn-info btn-xs" title="Docs">
|
|
36
36
|
{% else %}
|
|
37
|
-
<a
|
|
37
|
+
<a class="btn btn-info btn-xs disabled" aria-disabled="true" title="No docs provided">
|
|
38
38
|
{% endif %}
|
|
39
39
|
<i class="mdi mdi-book-open-page-variant"></i>
|
|
40
40
|
</a>
|
nautobot/extras/plugins/urls.py
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
from django.apps import apps
|
|
2
|
-
from django.conf import settings
|
|
3
|
-
from django.conf.urls import include
|
|
4
1
|
from django.urls import path
|
|
5
2
|
|
|
6
|
-
from nautobot.extras.plugins.utils import import_object
|
|
7
|
-
|
|
8
3
|
from . import views
|
|
9
4
|
|
|
5
|
+
BASE_URL_TO_APP_LABEL = {}
|
|
6
|
+
|
|
10
7
|
# Initialize URL base, API, and admin URL patterns for plugins
|
|
11
8
|
apps_patterns = [
|
|
12
9
|
path("installed-apps/", views.InstalledAppsView.as_view(), name="apps_list"),
|
|
@@ -30,19 +27,3 @@ plugin_api_patterns = [
|
|
|
30
27
|
),
|
|
31
28
|
]
|
|
32
29
|
plugin_admin_patterns = []
|
|
33
|
-
|
|
34
|
-
# Register base/API URL patterns for each plugin
|
|
35
|
-
for plugin_path in settings.PLUGINS:
|
|
36
|
-
plugin_name = plugin_path.split(".")[-1]
|
|
37
|
-
app = apps.get_app_config(plugin_name)
|
|
38
|
-
base_url = app.base_url or app.label
|
|
39
|
-
|
|
40
|
-
# Check if the plugin specifies any base URLs
|
|
41
|
-
urlpatterns = import_object(f"{plugin_path}.urls.urlpatterns")
|
|
42
|
-
if urlpatterns is not None:
|
|
43
|
-
plugin_patterns.append(path(f"{base_url}/", include((urlpatterns, app.label))))
|
|
44
|
-
|
|
45
|
-
# Check if the plugin specifies any API URLs
|
|
46
|
-
urlpatterns = import_object(f"{plugin_path}.api.urls.urlpatterns")
|
|
47
|
-
if urlpatterns is not None:
|
|
48
|
-
plugin_api_patterns.append(path(f"{base_url}/", include((urlpatterns, f"{app.label}-api"))))
|