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/plugins/utils.py
CHANGED
|
@@ -4,7 +4,6 @@ Plugin utilities.
|
|
|
4
4
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
import logging
|
|
7
|
-
import sys
|
|
8
7
|
|
|
9
8
|
from django.core.exceptions import ImproperlyConfigured
|
|
10
9
|
from django.utils.module_loading import import_string
|
|
@@ -17,37 +16,6 @@ from .exceptions import PluginImproperlyConfigured, PluginNotFound
|
|
|
17
16
|
logger = logging.getLogger(__name__)
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
def import_object(module_and_object):
|
|
21
|
-
"""
|
|
22
|
-
Import a specific object from a specific module by name, such as "nautobot.extras.plugins.utils.import_object".
|
|
23
|
-
|
|
24
|
-
Returns the imported object, or None if it doesn't exist.
|
|
25
|
-
"""
|
|
26
|
-
target_module_name, object_name = module_and_object.rsplit(".", 1)
|
|
27
|
-
module_hierarchy = target_module_name.split(".")
|
|
28
|
-
|
|
29
|
-
# Iterate through the module hierarchy, checking for the existence of each successive submodule.
|
|
30
|
-
# We have to do this rather than jumping directly to calling find_spec(target_module_name)
|
|
31
|
-
# because find_spec will raise a ModuleNotFoundError if any parent module of target_module_name does not exist.
|
|
32
|
-
module_name = ""
|
|
33
|
-
for module_component in module_hierarchy:
|
|
34
|
-
module_name = f"{module_name}.{module_component}" if module_name else module_component
|
|
35
|
-
spec = importlib.util.find_spec(module_name)
|
|
36
|
-
if spec is None:
|
|
37
|
-
# No such module
|
|
38
|
-
return None
|
|
39
|
-
|
|
40
|
-
# Okay, target_module_name exists. Load it if not already loaded
|
|
41
|
-
if target_module_name in sys.modules:
|
|
42
|
-
module = sys.modules[target_module_name]
|
|
43
|
-
else:
|
|
44
|
-
module = importlib.util.module_from_spec(spec)
|
|
45
|
-
sys.modules[target_module_name] = module
|
|
46
|
-
spec.loader.exec_module(module)
|
|
47
|
-
|
|
48
|
-
return getattr(module, object_name, None)
|
|
49
|
-
|
|
50
|
-
|
|
51
19
|
def load_plugins(settings):
|
|
52
20
|
"""Process plugins and log errors if they can't be loaded."""
|
|
53
21
|
for plugin_name in settings.PLUGINS:
|
|
@@ -77,7 +45,7 @@ def load_plugin(plugin_name, settings):
|
|
|
77
45
|
except AttributeError as err:
|
|
78
46
|
raise PluginImproperlyConfigured(
|
|
79
47
|
f"Plugin {plugin_name} does not provide a 'config' variable. This should be defined in the plugin's "
|
|
80
|
-
f"__init__.py file and point to the
|
|
48
|
+
f"__init__.py file and point to the NautobotAppConfig subclass."
|
|
81
49
|
) from err
|
|
82
50
|
|
|
83
51
|
# Validate user-provided configuration settings and assign defaults. Plugin
|
nautobot/extras/plugins/views.py
CHANGED
|
@@ -86,7 +86,6 @@ class InstalledAppsView(GenericView):
|
|
|
86
86
|
"""
|
|
87
87
|
|
|
88
88
|
table = InstalledAppsTable
|
|
89
|
-
breadcrumbs = Breadcrumbs(items={"*": [ViewNameBreadcrumbItem(view_name="apps:apps_list", label="Installed Apps")]})
|
|
90
89
|
view_titles = Titles(titles={"*": "Installed Apps"})
|
|
91
90
|
|
|
92
91
|
def get(self, request):
|
|
@@ -142,11 +141,6 @@ class InstalledAppDetailView(GenericView):
|
|
|
142
141
|
items={
|
|
143
142
|
"*": [
|
|
144
143
|
ViewNameBreadcrumbItem(view_name="apps:apps_list", label="Installed Apps"),
|
|
145
|
-
ViewNameBreadcrumbItem(
|
|
146
|
-
view_name="apps:app_detail",
|
|
147
|
-
reverse_kwargs=lambda context: {"app": context["app_data"]["package"]},
|
|
148
|
-
label=lambda context: context["app_data"]["name"],
|
|
149
|
-
),
|
|
150
144
|
]
|
|
151
145
|
}
|
|
152
146
|
)
|
|
@@ -250,9 +244,6 @@ class MarketplaceView(GenericView):
|
|
|
250
244
|
View for listing all available Apps.
|
|
251
245
|
"""
|
|
252
246
|
|
|
253
|
-
breadcrumbs = Breadcrumbs(
|
|
254
|
-
items={"generic": [ViewNameBreadcrumbItem(view_name="apps:apps_marketplace", label="Apps Marketplace")]}
|
|
255
|
-
)
|
|
256
247
|
view_titles = Titles(titles={"generic": "Apps Marketplace"})
|
|
257
248
|
|
|
258
249
|
def get(self, request):
|
nautobot/extras/querysets.py
CHANGED
|
@@ -19,6 +19,9 @@ class ConfigContextQuerySet(RestrictedQuerySet):
|
|
|
19
19
|
# `device_type` for Device; `type` for VirtualMachine
|
|
20
20
|
device_type = getattr(obj, "device_type", None)
|
|
21
21
|
|
|
22
|
+
# `device_family` for Device;
|
|
23
|
+
device_family = getattr(device_type, "device_family", None)
|
|
24
|
+
|
|
22
25
|
device_redundancy_group = getattr(obj, "device_redundancy_group", None)
|
|
23
26
|
|
|
24
27
|
# Get the group of the assigned tenant, if any
|
|
@@ -40,6 +43,7 @@ class ConfigContextQuerySet(RestrictedQuerySet):
|
|
|
40
43
|
Q(locations__in=locations) | Q(locations=None),
|
|
41
44
|
Q(roles=role) | Q(roles=None),
|
|
42
45
|
Q(device_types=device_type) | Q(device_types=None),
|
|
46
|
+
Q(device_families=device_family) | Q(device_families=None),
|
|
43
47
|
Q(platforms=obj.platform) | Q(platforms=None),
|
|
44
48
|
Q(device_redundancy_groups=device_redundancy_group) | Q(device_redundancy_groups=None),
|
|
45
49
|
Q(tenant_groups__in=tenant_groups) | Q(tenant_groups=None),
|
|
@@ -136,6 +140,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
|
|
|
136
140
|
if self.model._meta.model_name == "device":
|
|
137
141
|
location_query_string = "location"
|
|
138
142
|
base_query.add((Q(device_types=OuterRef("device_type")) | Q(device_types=None)), Q.AND)
|
|
143
|
+
base_query.add((Q(device_families=OuterRef("device_type__device_family")) | Q(device_families=None)), Q.AND)
|
|
139
144
|
base_query.add(
|
|
140
145
|
(Q(device_redundancy_groups=OuterRef("device_redundancy_group")) | Q(device_redundancy_groups=None)),
|
|
141
146
|
Q.AND,
|
|
@@ -145,6 +150,9 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
|
|
|
145
150
|
base_query.add((Q(cluster_groups=OuterRef("clusters__cluster_group")) | Q(cluster_groups=None)), Q.AND)
|
|
146
151
|
else:
|
|
147
152
|
location_query_string = "cluster__location"
|
|
153
|
+
base_query.add(Q(device_types=None), Q.AND)
|
|
154
|
+
base_query.add(Q(device_families=None), Q.AND)
|
|
155
|
+
base_query.add(Q(device_redundancy_groups=None), Q.AND)
|
|
148
156
|
# For virtual machines, handle cluster as ForeignKey relationship
|
|
149
157
|
base_query.add((Q(clusters=OuterRef("cluster")) | Q(clusters=None)), Q.AND)
|
|
150
158
|
base_query.add((Q(cluster_groups=OuterRef("cluster__cluster_group")) | Q(cluster_groups=None)), Q.AND)
|
nautobot/extras/signals.py
CHANGED
|
@@ -24,7 +24,7 @@ from nautobot.core.celery import app, import_jobs
|
|
|
24
24
|
from nautobot.core.models import BaseModel
|
|
25
25
|
from nautobot.core.utils.cache import construct_cache_key
|
|
26
26
|
from nautobot.core.utils.logging import sanitize
|
|
27
|
-
from nautobot.extras.choices import JobResultStatusChoices, ObjectChangeActionChoices
|
|
27
|
+
from nautobot.extras.choices import ButtonClassChoices, JobResultStatusChoices, ObjectChangeActionChoices
|
|
28
28
|
from nautobot.extras.constants import CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL
|
|
29
29
|
from nautobot.extras.models import (
|
|
30
30
|
ComputedField,
|
|
@@ -557,24 +557,6 @@ m2m_changed.connect(dynamic_group_children_changed, sender=DynamicGroup.children
|
|
|
557
557
|
pre_save.connect(dynamic_group_membership_created, sender=DynamicGroupMembership)
|
|
558
558
|
|
|
559
559
|
|
|
560
|
-
def dynamic_group_update_cached_members(sender, instance, **kwargs):
|
|
561
|
-
"""
|
|
562
|
-
When a DynamicGroup or DynamicGroupMembership is updated, update the cache of members for it and any parent groups.
|
|
563
|
-
"""
|
|
564
|
-
if isinstance(instance, DynamicGroupMembership):
|
|
565
|
-
group = instance.parent_group
|
|
566
|
-
else:
|
|
567
|
-
group = instance
|
|
568
|
-
|
|
569
|
-
group.update_cached_members()
|
|
570
|
-
for ancestor in group.get_ancestors():
|
|
571
|
-
ancestor.update_cached_members()
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
post_save.connect(dynamic_group_update_cached_members, sender=DynamicGroup)
|
|
575
|
-
post_save.connect(dynamic_group_update_cached_members, sender=DynamicGroupMembership)
|
|
576
|
-
|
|
577
|
-
|
|
578
560
|
#
|
|
579
561
|
# Jobs
|
|
580
562
|
#
|
|
@@ -659,6 +641,25 @@ def refresh_job_models(sender, *, apps, **kwargs):
|
|
|
659
641
|
job_model.installed = False
|
|
660
642
|
job_model.save()
|
|
661
643
|
|
|
644
|
+
# Wire up the JobButton for Dynamic Group member refresh
|
|
645
|
+
JobButton = apps.get_model("extras", "JobButton")
|
|
646
|
+
ContentType = apps.get_model("contenttypes", "ContentType") # pylint: disable=redefined-outer-name
|
|
647
|
+
DynamicGroup = apps.get_model("extras", "DynamicGroup") # pylint: disable=redefined-outer-name
|
|
648
|
+
|
|
649
|
+
dg_job_button, _ = JobButton.objects.get_or_create(
|
|
650
|
+
name="Refresh Dynamic Group Members Cache",
|
|
651
|
+
job=Job.objects.get(
|
|
652
|
+
module_name="nautobot.core.jobs.groups", job_class_name="RefreshDynamicGroupCacheJobButtonReceiver"
|
|
653
|
+
),
|
|
654
|
+
defaults={
|
|
655
|
+
"enabled": True,
|
|
656
|
+
"text": "Refresh Members",
|
|
657
|
+
"button_class": ButtonClassChoices.CLASS_WARNING,
|
|
658
|
+
"confirmation": True,
|
|
659
|
+
},
|
|
660
|
+
)
|
|
661
|
+
dg_job_button.content_types.add(ContentType.objects.get_for_model(DynamicGroup))
|
|
662
|
+
|
|
662
663
|
|
|
663
664
|
#
|
|
664
665
|
# Metadata
|
nautobot/extras/tables.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from textwrap import dedent
|
|
3
|
+
|
|
1
4
|
from django.conf import settings
|
|
2
|
-
from django.
|
|
5
|
+
from django.contrib.contenttypes.models import ContentType
|
|
3
6
|
from django.utils.html import format_html, format_html_join
|
|
4
7
|
import django_tables2 as tables
|
|
5
|
-
from django_tables2.data import TableData
|
|
6
|
-
from django_tables2.rows import BoundRows
|
|
7
8
|
from django_tables2.utils import Accessor
|
|
8
9
|
from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
|
|
9
10
|
|
|
10
|
-
from nautobot.core.models.querysets import count_related
|
|
11
11
|
from nautobot.core.tables import (
|
|
12
12
|
ApprovalButtonsColumn,
|
|
13
13
|
BaseTable,
|
|
@@ -24,7 +24,7 @@ from nautobot.core.tables import (
|
|
|
24
24
|
from nautobot.core.templatetags.helpers import HTML_NONE, render_boolean, render_json, render_markdown
|
|
25
25
|
from nautobot.tenancy.tables import TenantColumn
|
|
26
26
|
|
|
27
|
-
from .choices import
|
|
27
|
+
from .choices import JobResultStatusChoices, MetadataTypeDataTypeChoices
|
|
28
28
|
from .models import (
|
|
29
29
|
ApprovalWorkflow,
|
|
30
30
|
ApprovalWorkflowDefinition,
|
|
@@ -73,6 +73,8 @@ from .models import (
|
|
|
73
73
|
)
|
|
74
74
|
from .registry import registry
|
|
75
75
|
|
|
76
|
+
logger = logging.getLogger(__name__)
|
|
77
|
+
|
|
76
78
|
APPROVAL_WORKFLOW_OBJECT = """
|
|
77
79
|
{% if record.object_under_review and record.object_under_review.get_absolute_url %}
|
|
78
80
|
<a href="{{ record.object_under_review.get_absolute_url }}">{{ record.object_under_review }}</a>
|
|
@@ -129,7 +131,7 @@ GITREPOSITORY_BUTTONS = """
|
|
|
129
131
|
<button
|
|
130
132
|
data-url="{% url 'extras:gitrepository_sync' pk=record.pk %}"
|
|
131
133
|
type="submit"
|
|
132
|
-
class="dropdown-item sync-repository{% if perms.extras.change_gitrepository %} text-primary"{% else %}" disabled
|
|
134
|
+
class="dropdown-item sync-repository{% if perms.extras.change_gitrepository %} text-primary"{% else %}" disabled{% endif %}
|
|
133
135
|
>
|
|
134
136
|
<span class="mdi mdi-source-branch-sync" aria-hidden="true"></span>
|
|
135
137
|
Sync
|
|
@@ -168,7 +170,7 @@ JOB_RESULT_BUTTONS = """
|
|
|
168
170
|
</li>
|
|
169
171
|
{% else %}
|
|
170
172
|
<li>
|
|
171
|
-
<a
|
|
173
|
+
<a class="dropdown-item disabled" aria-disabled="true">
|
|
172
174
|
<span class="mdi mdi-repeat-off" aria-hidden="true"></span>
|
|
173
175
|
Job is not available, cannot be re-run
|
|
174
176
|
</a>
|
|
@@ -220,7 +222,7 @@ SCHEDULED_JOB_APPROVAL_QUEUE_BUTTONS = """
|
|
|
220
222
|
<button
|
|
221
223
|
type="button"
|
|
222
224
|
onClick="handleDetailPostAction('{% url 'extras:scheduledjob_approval_request_view' pk=record.pk %}', '_dry_run')"
|
|
223
|
-
class="dropdown-item{% if perms.extras.run_job and record.job_model.supports_dryrun %} text-primary"{% else %}" disabled
|
|
225
|
+
class="dropdown-item{% if perms.extras.run_job and record.job_model.supports_dryrun %} text-primary"{% else %}" disabled{% endif %}
|
|
224
226
|
>
|
|
225
227
|
<span class="mdi mdi-play" aria-hidden="true"></span>
|
|
226
228
|
Dry Run
|
|
@@ -230,7 +232,7 @@ SCHEDULED_JOB_APPROVAL_QUEUE_BUTTONS = """
|
|
|
230
232
|
<button
|
|
231
233
|
type="button"
|
|
232
234
|
onClick="handleDetailPostAction('{% url 'extras:scheduledjob_approval_request_view' pk=record.pk %}', '_approve')"
|
|
233
|
-
class="dropdown-item{% if perms.extras.run_job %} text-success"{% else %}" disabled
|
|
235
|
+
class="dropdown-item{% if perms.extras.run_job %} text-success"{% else %}" disabled{% endif %}
|
|
234
236
|
>
|
|
235
237
|
<span class="mdi mdi-check" aria-hidden="true"></span>
|
|
236
238
|
Approve
|
|
@@ -240,7 +242,7 @@ SCHEDULED_JOB_APPROVAL_QUEUE_BUTTONS = """
|
|
|
240
242
|
<button
|
|
241
243
|
type="button"
|
|
242
244
|
onClick="handleDetailPostAction('{% url 'extras:scheduledjob_approval_request_view' pk=record.pk %}', '_deny')"
|
|
243
|
-
class="dropdown-item{% if perms.extras.run_job %} text-danger"{% else %}" disabled
|
|
245
|
+
class="dropdown-item{% if perms.extras.run_job %} text-danger"{% else %}" disabled{% endif %}
|
|
244
246
|
>
|
|
245
247
|
<span class="mdi mdi-close" aria-hidden="true"></span>
|
|
246
248
|
Deny
|
|
@@ -294,7 +296,7 @@ class ApprovalWorkflowStageDefinitionTable(BaseTable):
|
|
|
294
296
|
fields = (
|
|
295
297
|
"pk",
|
|
296
298
|
"approval_workflow_definition",
|
|
297
|
-
"
|
|
299
|
+
"sequence",
|
|
298
300
|
"name",
|
|
299
301
|
"min_approvers",
|
|
300
302
|
"denial_message",
|
|
@@ -303,7 +305,7 @@ class ApprovalWorkflowStageDefinitionTable(BaseTable):
|
|
|
303
305
|
default_columns = (
|
|
304
306
|
"pk",
|
|
305
307
|
"approval_workflow_definition",
|
|
306
|
-
"
|
|
308
|
+
"sequence",
|
|
307
309
|
"name",
|
|
308
310
|
"min_approvers",
|
|
309
311
|
"denial_message",
|
|
@@ -1065,8 +1067,8 @@ def log_object_link(value, record):
|
|
|
1065
1067
|
|
|
1066
1068
|
def log_entry_color_css(record):
|
|
1067
1069
|
if record.log_level.lower() in ("failure", "error", "critical"):
|
|
1068
|
-
return "danger"
|
|
1069
|
-
return record.log_level.lower()
|
|
1070
|
+
return "table-danger"
|
|
1071
|
+
return "table-" + record.log_level.lower()
|
|
1070
1072
|
|
|
1071
1073
|
|
|
1072
1074
|
class JobTable(BaseTable):
|
|
@@ -1159,6 +1161,7 @@ class JobTable(BaseTable):
|
|
|
1159
1161
|
|
|
1160
1162
|
class JobHookTable(BaseTable):
|
|
1161
1163
|
pk = ToggleColumn()
|
|
1164
|
+
enabled = BooleanColumn()
|
|
1162
1165
|
name = tables.Column(linkify=True)
|
|
1163
1166
|
content_types = tables.TemplateColumn(WEBHOOK_CONTENT_TYPES)
|
|
1164
1167
|
job = tables.Column(linkify=True)
|
|
@@ -1270,62 +1273,20 @@ class JobResultTable(BaseTable):
|
|
|
1270
1273
|
duration = tables.Column(orderable=False)
|
|
1271
1274
|
actions = ButtonsColumn(JobResult, buttons=("delete",), prepend_template=JOB_RESULT_BUTTONS)
|
|
1272
1275
|
|
|
1273
|
-
def __init__(self, *args, **kwargs):
|
|
1274
|
-
super().__init__(*args, **kwargs)
|
|
1275
|
-
# Only calculate log counts for "summary" column if it's actually visible.
|
|
1276
|
-
if "summary" in self.columns and self.columns["summary"].visible and isinstance(self.data.data, QuerySet):
|
|
1277
|
-
self.data = TableData.from_data(
|
|
1278
|
-
self.data.data.annotate(
|
|
1279
|
-
debug_log_count=count_related(
|
|
1280
|
-
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
|
|
1281
|
-
),
|
|
1282
|
-
success_log_count=count_related(
|
|
1283
|
-
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
|
|
1284
|
-
),
|
|
1285
|
-
info_log_count=count_related(
|
|
1286
|
-
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
|
|
1287
|
-
),
|
|
1288
|
-
warning_log_count=count_related(
|
|
1289
|
-
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_WARNING}
|
|
1290
|
-
),
|
|
1291
|
-
error_log_count=count_related(
|
|
1292
|
-
JobLogEntry,
|
|
1293
|
-
"job_result",
|
|
1294
|
-
filter_dict={
|
|
1295
|
-
"log_level__in": [
|
|
1296
|
-
LogLevelChoices.LOG_FAILURE,
|
|
1297
|
-
LogLevelChoices.LOG_ERROR,
|
|
1298
|
-
LogLevelChoices.LOG_CRITICAL,
|
|
1299
|
-
],
|
|
1300
|
-
},
|
|
1301
|
-
),
|
|
1302
|
-
)
|
|
1303
|
-
)
|
|
1304
|
-
self.data.set_table(self)
|
|
1305
|
-
self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data)
|
|
1306
|
-
|
|
1307
1276
|
def render_summary(self, record):
|
|
1308
1277
|
"""
|
|
1309
1278
|
Define custom rendering for the summary column.
|
|
1310
1279
|
"""
|
|
1311
|
-
#
|
|
1312
|
-
#
|
|
1313
|
-
if not
|
|
1314
|
-
record.debug_log_count
|
|
1315
|
-
|
|
1316
|
-
record.
|
|
1317
|
-
|
|
1318
|
-
record.
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
if not hasattr(record, "error_log_count"):
|
|
1322
|
-
record.error_log_count = record.job_log_entries.filter(
|
|
1323
|
-
log_level__in=[
|
|
1324
|
-
LogLevelChoices.LOG_FAILURE,
|
|
1325
|
-
LogLevelChoices.LOG_ERROR,
|
|
1326
|
-
LogLevelChoices.LOG_CRITICAL,
|
|
1327
|
-
]
|
|
1328
|
-
).count()
|
|
1280
|
+
# The *_log_count attributes will be calculated and updated at the end of a Job run when JobResult is saved.
|
|
1281
|
+
# If the values are not present due to a running Job or are missing in any field, skip display.
|
|
1282
|
+
if record.status not in JobResultStatusChoices.READY_STATES or None in [
|
|
1283
|
+
record.debug_log_count,
|
|
1284
|
+
record.success_log_count,
|
|
1285
|
+
record.info_log_count,
|
|
1286
|
+
record.warning_log_count,
|
|
1287
|
+
record.error_log_count,
|
|
1288
|
+
]:
|
|
1289
|
+
return ""
|
|
1329
1290
|
|
|
1330
1291
|
return format_html(
|
|
1331
1292
|
"""<label class="badge bg-secondary">{}</label>
|
|
@@ -1364,6 +1325,7 @@ class JobResultTable(BaseTable):
|
|
|
1364
1325
|
"job_model",
|
|
1365
1326
|
"user",
|
|
1366
1327
|
"status",
|
|
1328
|
+
"summary",
|
|
1367
1329
|
"actions",
|
|
1368
1330
|
)
|
|
1369
1331
|
|
|
@@ -1517,12 +1479,20 @@ class ScheduledJobTable(BaseTable):
|
|
|
1517
1479
|
pk = ToggleColumn()
|
|
1518
1480
|
name = tables.Column(linkify=True)
|
|
1519
1481
|
job_model = tables.Column(verbose_name="Job", linkify=True)
|
|
1482
|
+
enabled = BooleanColumn()
|
|
1520
1483
|
interval = tables.Column(verbose_name="Execution Type")
|
|
1521
1484
|
start_time = tables.DateTimeColumn(verbose_name="First Run", format=settings.SHORT_DATETIME_FORMAT)
|
|
1522
1485
|
last_run_at = tables.DateTimeColumn(verbose_name="Most Recent Run", format=settings.SHORT_DATETIME_FORMAT)
|
|
1523
1486
|
crontab = tables.Column()
|
|
1524
1487
|
total_run_count = tables.Column(verbose_name="Total Run Count")
|
|
1525
1488
|
actions = ButtonsColumn(ScheduledJob, buttons=("delete",), prepend_template=SCHEDULED_JOB_BUTTONS)
|
|
1489
|
+
approval_state = tables.Column(empty_values=[], orderable=False)
|
|
1490
|
+
|
|
1491
|
+
def render_approval_state(self, record):
|
|
1492
|
+
workflow = record.associated_approval_workflows.first()
|
|
1493
|
+
if workflow is not None:
|
|
1494
|
+
return format_html('<a href="{}">{}</a>', record.get_approval_workflow_url(), workflow.current_state)
|
|
1495
|
+
return HTML_NONE
|
|
1526
1496
|
|
|
1527
1497
|
class Meta(BaseTable.Meta):
|
|
1528
1498
|
model = ScheduledJob
|
|
@@ -1531,6 +1501,7 @@ class ScheduledJobTable(BaseTable):
|
|
|
1531
1501
|
"name",
|
|
1532
1502
|
"total_run_count",
|
|
1533
1503
|
"job_model",
|
|
1504
|
+
"approval_state",
|
|
1534
1505
|
"interval",
|
|
1535
1506
|
"start_time",
|
|
1536
1507
|
"last_run_at",
|
|
@@ -1542,6 +1513,8 @@ class ScheduledJobTable(BaseTable):
|
|
|
1542
1513
|
"pk",
|
|
1543
1514
|
"name",
|
|
1544
1515
|
"job_model",
|
|
1516
|
+
"enabled",
|
|
1517
|
+
"approval_state",
|
|
1545
1518
|
"interval",
|
|
1546
1519
|
"last_run_at",
|
|
1547
1520
|
"actions",
|
|
@@ -1588,8 +1561,31 @@ class ObjectChangeTable(BaseTable):
|
|
|
1588
1561
|
|
|
1589
1562
|
def __init__(self, *args, **kwargs):
|
|
1590
1563
|
super().__init__(*args, **kwargs)
|
|
1591
|
-
#
|
|
1592
|
-
|
|
1564
|
+
# Only prefetch if all content types are valid
|
|
1565
|
+
if all(ct.model_class() is not None for ct in ContentType.objects.all()):
|
|
1566
|
+
self.add_conditional_prefetch("object_repr", "changed_object")
|
|
1567
|
+
else:
|
|
1568
|
+
error_message = dedent("""\
|
|
1569
|
+
One or more ContentType entries in the database are invalid.
|
|
1570
|
+
This will likely cause performance degradation when viewing the Object Change log.
|
|
1571
|
+
An administrator can follow these steps to resolve common issues:
|
|
1572
|
+
- Run `nautobot-server remove_stale_contenttypes`
|
|
1573
|
+
- Run `nautobot-server migrate <app_label> zero` for any app labels which no longer exist
|
|
1574
|
+
- Manually dropping tables for any models which have been removed from Nautobot or its plugins from your database
|
|
1575
|
+
- Run ```
|
|
1576
|
+
from django.contrib.contenttypes.models import ContentType
|
|
1577
|
+
qs = ContentType.objects.filter(
|
|
1578
|
+
app_label__in=[
|
|
1579
|
+
"<app_label_of_removed_plugin_1>",
|
|
1580
|
+
"<app_label_of_removed_plugin_2>",
|
|
1581
|
+
]
|
|
1582
|
+
) | ContentType.objects.filter(model__icontains="<name_of_removed_model_1>")
|
|
1583
|
+
# Review the queryset before running delete
|
|
1584
|
+
qs.delete()
|
|
1585
|
+
```
|
|
1586
|
+
Please ensure you fully understand the implications of these actions before proceeding.
|
|
1587
|
+
""")
|
|
1588
|
+
logger.warning(error_message)
|
|
1593
1589
|
|
|
1594
1590
|
|
|
1595
1591
|
#
|
|
@@ -38,6 +38,8 @@ Ref: https://github.com/nautobot/nautobot/issues/1289
|
|
|
38
38
|
|
|
39
39
|
_replace_links_{{ ajax_divname }}(url, div.getElementsByTagName('TH'));
|
|
40
40
|
_replace_links_{{ ajax_divname }}(url, div.getElementsByClassName('pagination'));
|
|
41
|
+
/* Hook back up the JS for the "per_page" dropdown, if present for this table */
|
|
42
|
+
initializeResultPerPageSelection(div);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
};
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
{% load helpers %}
|
|
3
3
|
|
|
4
4
|
{% block buttons %}
|
|
5
|
-
<div class="btn-group">
|
|
6
|
-
<a class="btn {% if approval_view %}
|
|
7
|
-
|
|
5
|
+
<div class="btn-group" role="group">
|
|
6
|
+
<a class="btn btn-primary{% if approval_view %} bg-primary nb-text-body-bg{% endif %}"
|
|
7
|
+
href="{% url 'extras:approver_dashboard' %}">
|
|
8
|
+
<span class="mdi mdi-checkbox-multiple-outline" aria-hidden="true"></span> My Approvals
|
|
8
9
|
</a>
|
|
9
|
-
<a class="btn {% if not approval_view %}
|
|
10
|
-
|
|
10
|
+
<a class="btn btn-primary{% if not approval_view %} bg-primary nb-text-body-bg{% endif %}"
|
|
11
|
+
href="{% url 'extras:approvee_dashboard' %}">
|
|
12
|
+
<span class="mdi mdi-help-box-multiple-outline" aria-hidden="true"></span> My Requests
|
|
11
13
|
</a>
|
|
12
14
|
</div>
|
|
13
15
|
{% endblock %}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{% render_field form.name %}
|
|
10
10
|
{% render_field form.model_content_type %}
|
|
11
11
|
{% render_field form.model_constraints %}
|
|
12
|
-
{% render_field form.
|
|
12
|
+
{% render_field form.weight %}
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
<div class="card">
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
<thead>
|
|
23
23
|
<tr>
|
|
24
24
|
{% for field in stage.visible_fields %}
|
|
25
|
-
<th>
|
|
25
|
+
<th>
|
|
26
|
+
<span data-bs-toggle="tooltip" data-bs-placement="top" title="{{ field.help_text }}">{{ field.label|capfirst }}</span>
|
|
27
|
+
</th>
|
|
26
28
|
{% endfor %}
|
|
27
29
|
</tr>
|
|
28
30
|
</thead>
|
|
@@ -3,19 +3,27 @@
|
|
|
3
3
|
|
|
4
4
|
{% block buttons %}
|
|
5
5
|
<span title="{% if not object.is_active_stage %}This stage is not active{% elif request.user in object.users_that_already_approved %}You already approved this stage{% else %}Approve this stage{% endif %}">
|
|
6
|
-
|
|
7
|
-
href="{% url 'extras:approvalworkflowstage_approve' object.pk %}?return_url={{request.path}}"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
{% if object.is_active_stage and request.user not in object.users_that_already_approved %}
|
|
7
|
+
<a href="{% url 'extras:approvalworkflowstage_approve' object.pk %}?return_url={{request.path}}"
|
|
8
|
+
class="btn btn-success">
|
|
9
|
+
<span class="mdi mdi-check"></span> Approve
|
|
10
|
+
</a>
|
|
11
|
+
{% else %}
|
|
12
|
+
<a aria-disabled="true" class="btn btn-success disabled">
|
|
13
|
+
<span class="mdi mdi-check"></span> Approve
|
|
14
|
+
</a>
|
|
15
|
+
{% endif %}
|
|
12
16
|
</span>
|
|
13
17
|
<span title="{% if not object.is_active_stage %}This stage is not active{% elif request.user in object.users_that_already_approved %}You already approved this stage{% else %}Deny this stage{% endif %}">
|
|
14
|
-
|
|
15
|
-
href="{% url 'extras:approvalworkflowstage_deny' object.pk %}?return_url={{request.path}}"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
{% if object.is_active_stage and request.user not in object.users_that_already_approved %}
|
|
19
|
+
<a href="{% url 'extras:approvalworkflowstage_deny' object.pk %}?return_url={{request.path}}"
|
|
20
|
+
class="btn btn-danger">
|
|
21
|
+
<span class="mdi mdi-close"></span> Deny
|
|
22
|
+
</a>
|
|
23
|
+
{% else %}
|
|
24
|
+
<a aria-disabled="true" class="btn btn-danger disabled">
|
|
25
|
+
<span class="mdi mdi-close"></span> Deny
|
|
26
|
+
</a>
|
|
27
|
+
{% endif %}
|
|
20
28
|
</span>
|
|
21
29
|
{% endblock buttons %}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
{% render_field form.locations %}
|
|
19
19
|
{% render_field form.roles %}
|
|
20
20
|
{% render_field form.device_types %}
|
|
21
|
+
{% render_field form.device_families %}
|
|
21
22
|
{% render_field form.platforms %}
|
|
22
23
|
{% render_field form.cluster_groups %}
|
|
23
24
|
{% render_field form.clusters %}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{% extends 'extras/
|
|
1
|
+
{% extends 'extras/configcontextschema_retrieve.html' %}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
{% block title %}{{ object }} - Validation{% endblock %}
|
|
4
4
|
|
|
5
5
|
{% block content %}
|
|
6
6
|
<div class="row">
|
|
@@ -11,15 +11,21 @@
|
|
|
11
11
|
|
|
12
12
|
{% block extra_tab_content %}
|
|
13
13
|
<div id="members" role="tabpanel" class="tab-pane {% if not active_tab and not request.GET.tab or request.GET.tab == "members" %}active{% else %}fade{% endif %}">
|
|
14
|
-
|
|
15
|
-
<div class="
|
|
16
|
-
|
|
14
|
+
<div class="row">
|
|
15
|
+
<div class="col-lg-12">
|
|
16
|
+
{% if members_list_url %}
|
|
17
17
|
<div class="alert alert-success" role="alert">
|
|
18
18
|
You can bulk-add and bulk-remove members of this group from the <a href="{{ members_list_url }}">{{ members_verbose_name_plural|bettertitle }} list view</a>.
|
|
19
19
|
</div>
|
|
20
|
-
|
|
20
|
+
{% else %}
|
|
21
|
+
<div class="alert alert-warning" role="alert">
|
|
22
|
+
Dynamic group membership is cached for performance reasons, therefore this listing may not always
|
|
23
|
+
be up-to-date.<br>You can refresh the membership of this group asynchronously by clicking the
|
|
24
|
+
"Refresh Members" button above.
|
|
25
|
+
</div>
|
|
26
|
+
{% endif %}
|
|
21
27
|
</div>
|
|
22
|
-
|
|
28
|
+
</div>
|
|
23
29
|
<div class="row">
|
|
24
30
|
<div class="col-lg-12">
|
|
25
31
|
{% include 'utilities/obj_table.html' with table=members_table table_template='panel_table.html' heading='Dynamic Group members' %}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
<div class="tab-content">
|
|
44
44
|
<div class="tab-pane active" id="filter-form">
|
|
45
45
|
{% if filter_form %}
|
|
46
|
-
<span class="form-text">
|
|
46
|
+
<span class="form-text mb-12">
|
|
47
47
|
Select the filtering criteria to determine membership of objects matching
|
|
48
48
|
the Content Type for this Dynamic Group. Fields that are not a dropdown are
|
|
49
49
|
expected to have string inputs and do not support multiple values.
|
|
@@ -10,15 +10,29 @@
|
|
|
10
10
|
{% endif %}
|
|
11
11
|
{% if "approve" in buttons and have_permission and record.is_active_stage %}
|
|
12
12
|
<span title='{% if request.user in record.users_that_already_approved %}You already approved this stage{% else %}Approve this stage{% endif %}'>
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
13
|
+
{% if request.user not in record.users_that_already_approved %}
|
|
14
|
+
<a href="{% url approval_route pk=record.pk %}?return_url={{ request.path }}{{ return_url_extra }}"
|
|
15
|
+
class="btn btn-success btn-sm">
|
|
16
|
+
<span class="mdi mdi-check"></span>
|
|
17
|
+
</a>
|
|
18
|
+
{% else %}
|
|
19
|
+
<a aria-disabled="true" class="btn btn-success btn-sm disabled">
|
|
20
|
+
<span class="mdi mdi-check"></span>
|
|
21
|
+
</a>
|
|
22
|
+
{% endif %}
|
|
16
23
|
</span>
|
|
17
24
|
{% endif %}
|
|
18
25
|
{% if "deny" in buttons and have_permission and record.is_active_stage and request.user not in record.users_that_already_approved %}
|
|
19
26
|
<span title='{%if request.user in record.users_that_already_approved %}You already approved this stage{% else %}Deny this stage{% endif %}'>
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
27
|
+
{% if request.user not in record.users_that_already_approved %}
|
|
28
|
+
<a href="{% url deny_route pk=record.pk %}?return_url={{ request.path }}{{ return_url_extra }}"
|
|
29
|
+
class="btn btn-danger btn-sm">
|
|
30
|
+
<span class="mdi mdi-close"></span>
|
|
31
|
+
</a>
|
|
32
|
+
{% else %}
|
|
33
|
+
<a aria-disabled="true" class="btn btn-danger btn-sm disabled">
|
|
34
|
+
<span class="mdi mdi-close"></span>
|
|
35
|
+
</a>
|
|
36
|
+
{% endif %}
|
|
23
37
|
</span>
|
|
24
38
|
{% endif %}
|