nautobot 2.4.2__py3-none-any.whl → 2.4.4__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/filters.py +2 -0
- nautobot/circuits/filters.py +1 -1
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +1 -1
- nautobot/circuits/tests/integration/test_circuit.py +135 -0
- nautobot/circuits/tests/test_models.py +5 -3
- nautobot/circuits/views.py +4 -1
- nautobot/cloud/api/views.py +3 -3
- nautobot/cloud/filters.py +3 -6
- nautobot/cloud/tests/test_filters.py +21 -0
- nautobot/core/admin.py +2 -0
- nautobot/core/constants.py +0 -1
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/forms.py +2 -1
- nautobot/core/forms/widgets.py +8 -0
- nautobot/core/jobs/__init__.py +2 -1
- nautobot/core/management/commands/generate_performance_test_endpoints.py +271 -0
- nautobot/core/models/utils.py +6 -1
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- 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_create.html +5 -0
- 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/inc/javascript.html +3 -0
- nautobot/core/templates/widgets/clearable_file.html +5 -0
- nautobot/core/templatetags/helpers.py +3 -3
- nautobot/core/templatetags/ui_framework.py +20 -4
- nautobot/core/testing/forms.py +1 -1
- nautobot/core/testing/integration.py +37 -7
- nautobot/core/tests/test_api.py +1 -1
- nautobot/core/tests/test_commands.py +31 -0
- nautobot/core/tests/test_graphql.py +3 -3
- nautobot/core/tests/test_jobs.py +4 -1
- nautobot/core/tests/test_utils.py +17 -2
- nautobot/core/ui/object_detail.py +1 -1
- nautobot/core/utils/lookup.py +12 -1
- nautobot/core/views/generic.py +9 -1
- nautobot/core/views/mixins.py +9 -1
- nautobot/dcim/api/serializers.py +36 -0
- nautobot/dcim/api/views.py +12 -11
- nautobot/dcim/elevations.py +17 -4
- nautobot/dcim/factory.py +9 -1
- nautobot/dcim/filters/__init__.py +27 -1
- nautobot/dcim/forms.py +16 -7
- nautobot/dcim/models/devices.py +12 -7
- nautobot/dcim/signals.py +26 -0
- nautobot/dcim/templates/dcim/cable_trace.html +4 -4
- nautobot/dcim/templates/dcim/consoleport.html +14 -4
- nautobot/dcim/templates/dcim/consoleserverport.html +14 -4
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +3 -3
- nautobot/dcim/templates/dcim/frontport.html +7 -2
- nautobot/dcim/templates/dcim/interface.html +9 -4
- nautobot/dcim/templates/dcim/powerfeed.html +8 -3
- nautobot/dcim/templates/dcim/poweroutlet.html +14 -4
- nautobot/dcim/templates/dcim/powerport.html +14 -4
- nautobot/dcim/templates/dcim/rearport.html +7 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -62
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +6 -0
- nautobot/dcim/tests/integration/test_fileinputpicker.py +87 -0
- nautobot/dcim/tests/test_api.py +176 -0
- nautobot/dcim/tests/test_filters.py +56 -3
- nautobot/dcim/tests/test_models.py +41 -1
- nautobot/dcim/views.py +24 -14
- nautobot/extras/api/mixins.py +1 -1
- nautobot/extras/api/views.py +4 -4
- nautobot/extras/filters/__init__.py +4 -0
- nautobot/extras/forms/forms.py +4 -0
- nautobot/extras/jobs.py +8 -1
- nautobot/extras/models/datasources.py +7 -3
- nautobot/extras/plugins/__init__.py +26 -1
- nautobot/extras/templates/extras/inc/jobresult.html +12 -13
- nautobot/extras/templates/extras/job.html +1 -0
- nautobot/extras/templates/extras/objectchange.html +28 -12
- nautobot/extras/tests/test_api.py +16 -15
- nautobot/extras/tests/test_dynamicgroups.py +14 -0
- nautobot/extras/tests/test_filters.py +2 -0
- nautobot/extras/tests/test_plugins.py +32 -1
- nautobot/extras/tests/test_views.py +209 -11
- nautobot/extras/utils.py +30 -0
- nautobot/extras/views.py +32 -14
- nautobot/ipam/api/serializers.py +7 -8
- nautobot/ipam/api/views.py +5 -5
- nautobot/ipam/factory.py +27 -8
- nautobot/ipam/filters.py +67 -29
- nautobot/ipam/formfields.py +51 -0
- nautobot/ipam/forms.py +15 -7
- nautobot/ipam/migrations/0051_added_optional_vrf_relationship_to_vdc.py +41 -0
- nautobot/ipam/models.py +63 -5
- nautobot/ipam/tables.py +21 -7
- nautobot/ipam/tests/test_api.py +107 -66
- nautobot/ipam/tests/test_filters.py +145 -5
- nautobot/ipam/tests/test_views.py +15 -2
- nautobot/project-static/bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js +11 -0
- nautobot/project-static/css/base.css +11 -0
- nautobot/project-static/css/dark.css +2 -1
- nautobot/project-static/docs/apps/index.html +1 -1
- nautobot/project-static/docs/apps/nautobot-apps.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +62 -0
- nautobot/project-static/docs/development/apps/api/configuration-view.html +0 -3
- nautobot/project-static/docs/development/apps/api/models/graphql.html +9 -13
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +94 -1
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +2 -5
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +0 -3
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +0 -3
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +0 -3
- nautobot/project-static/docs/development/apps/api/prometheus.html +0 -3
- nautobot/project-static/docs/development/apps/api/setup.html +1 -1
- nautobot/project-static/docs/development/apps/api/testing.html +0 -6
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +0 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +0 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +0 -3
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +0 -3
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +1 -7
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +0 -7
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +0 -4
- nautobot/project-static/docs/development/apps/api/views/notes.html +0 -3
- nautobot/project-static/docs/development/apps/index.html +2 -35
- nautobot/project-static/docs/development/apps/migration/code-updates.html +7 -6
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +2 -2
- nautobot/project-static/docs/development/apps/migration/from-v1.html +3 -3
- nautobot/project-static/docs/development/core/application-registry.html +0 -6
- nautobot/project-static/docs/development/core/best-practices.html +1 -28
- nautobot/project-static/docs/development/core/bootstrap-ui.html +1 -1
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +65 -11
- nautobot/project-static/docs/development/core/getting-started.html +14 -18
- nautobot/project-static/docs/development/core/homepage.html +0 -3
- nautobot/project-static/docs/development/core/index.html +1 -1
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +3 -3
- nautobot/project-static/docs/development/core/model-checklist.html +1 -1
- nautobot/project-static/docs/development/core/navigation-menu.html +1 -1
- nautobot/project-static/docs/development/core/release-checklist.html +1 -1
- nautobot/project-static/docs/development/core/settings.html +1 -1
- nautobot/project-static/docs/development/core/style-guide.html +4 -9
- nautobot/project-static/docs/development/core/templates.html +0 -3
- nautobot/project-static/docs/development/core/testing.html +0 -9
- nautobot/project-static/docs/development/jobs/index.html +11 -30
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +3 -2
- nautobot/project-static/docs/index.html +3 -2
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +2 -20
- nautobot/project-static/docs/release-notes/version-1.0.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.1.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.2.html +3 -3
- nautobot/project-static/docs/release-notes/version-1.3.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.4.html +17 -17
- nautobot/project-static/docs/release-notes/version-1.5.html +8 -8
- nautobot/project-static/docs/release-notes/version-1.6.html +4 -4
- nautobot/project-static/docs/release-notes/version-2.0.html +10 -10
- nautobot/project-static/docs/release-notes/version-2.1.html +7 -7
- nautobot/project-static/docs/release-notes/version-2.2.html +1 -1
- nautobot/project-static/docs/release-notes/version-2.3.html +4 -4
- nautobot/project-static/docs/release-notes/version-2.4.html +379 -0
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +290 -290
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +3 -3
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +4 -4
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +1 -1
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +3 -13
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +5 -5
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +3 -18
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -4
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +15 -15
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/index.html +0 -16
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +7 -10
- nautobot/project-static/docs/user-guide/administration/installation/services.html +1 -12
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +3 -3
- nautobot/project-static/docs/user-guide/administration/security/index.html +1 -1
- nautobot/project-static/docs/user-guide/administration/security/notices.html +1 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +5 -35
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +12 -9
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +1 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +1 -7
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +0 -6
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +0 -8
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +15 -15
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +0 -6
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +3 -15
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1 -27
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +0 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +3 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +0 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +0 -7
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +6 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +0 -14
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +1 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +0 -14
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +0 -19
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +3 -9
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +0 -8
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +0 -4
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +1 -13
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +0 -5
- nautobot/project-static/js/dropdown.js +28 -0
- nautobot/project-static/js/editor.js +292 -0
- nautobot/project-static/monaco-editor-0.52.2/README.md +81 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/base/worker/workerMain.js +31 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/basic-languages/xml/xml.js +10 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/basic-languages/yaml/yaml.js +10 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/editor/editor.main.css +8 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/editor/editor.main.js +798 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/language/json/jsonMode.js +19 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/language/json/jsonWorker.js +42 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/loader.js +11 -0
- nautobot/tenancy/filters/__init__.py +3 -5
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/templates/tenancy/tenant_create.html +21 -0
- nautobot/tenancy/templates/tenancy/tenant_edit.html +2 -21
- nautobot/tenancy/templates/tenancy/tenantgroup.html +2 -44
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +1 -0
- nautobot/tenancy/tests/test_filters.py +10 -0
- nautobot/tenancy/tests/test_views.py +5 -1
- nautobot/tenancy/urls.py +7 -79
- nautobot/tenancy/views.py +51 -80
- nautobot/virtualization/views.py +0 -1
- nautobot/wireless/api/serializers.py +6 -1
- nautobot/wireless/api/views.py +3 -3
- nautobot/wireless/tables.py +9 -4
- nautobot/wireless/tests/test_api.py +5 -9
- {nautobot-2.4.2.dist-info → nautobot-2.4.4.dist-info}/METADATA +9 -9
- {nautobot-2.4.2.dist-info → nautobot-2.4.4.dist-info}/RECORD +267 -246
- {nautobot-2.4.2.dist-info → nautobot-2.4.4.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.2.dist-info → nautobot-2.4.4.dist-info}/NOTICE +0 -0
- {nautobot-2.4.2.dist-info → nautobot-2.4.4.dist-info}/WHEEL +0 -0
- {nautobot-2.4.2.dist-info → nautobot-2.4.4.dist-info}/entry_points.txt +0 -0
nautobot/extras/api/views.py
CHANGED
|
@@ -642,7 +642,7 @@ class JobViewSetBase(
|
|
|
642
642
|
):
|
|
643
643
|
raise ValidationError(
|
|
644
644
|
{
|
|
645
|
-
"_task_queue": "_task_queue and _job_queue are both specified. Please
|
|
645
|
+
"_task_queue": "_task_queue and _job_queue are both specified. Please specify only one or another."
|
|
646
646
|
}
|
|
647
647
|
)
|
|
648
648
|
|
|
@@ -685,7 +685,7 @@ class JobViewSetBase(
|
|
|
685
685
|
"job_queue", None
|
|
686
686
|
):
|
|
687
687
|
raise ValidationError(
|
|
688
|
-
{"task_queue": "task_queue and job_queue are both specified. Please
|
|
688
|
+
{"task_queue": "task_queue and job_queue are both specified. Please specify only one or another."}
|
|
689
689
|
)
|
|
690
690
|
schedule_data = input_serializer.validated_data.get("schedule", None)
|
|
691
691
|
|
|
@@ -836,7 +836,7 @@ class JobQueueViewSet(NautobotModelViewSet):
|
|
|
836
836
|
filterset_class = filters.JobQueueFilterSet
|
|
837
837
|
|
|
838
838
|
|
|
839
|
-
class JobQueueAssignmentViewSet(
|
|
839
|
+
class JobQueueAssignmentViewSet(ModelViewSet):
|
|
840
840
|
"""
|
|
841
841
|
Manage job queue assignments through DELETE, GET, POST, PUT, and PATCH requests.
|
|
842
842
|
"""
|
|
@@ -1069,7 +1069,7 @@ class MetadataChoiceViewSet(ModelViewSet):
|
|
|
1069
1069
|
filterset_class = filters.MetadataChoiceFilterSet
|
|
1070
1070
|
|
|
1071
1071
|
|
|
1072
|
-
class ObjectMetadataViewSet(
|
|
1072
|
+
class ObjectMetadataViewSet(ModelViewSet):
|
|
1073
1073
|
queryset = ObjectMetadata.objects.all()
|
|
1074
1074
|
serializer_class = serializers.ObjectMetadataSerializer
|
|
1075
1075
|
filterset_class = filters.ObjectMetadataFilterSet
|
|
@@ -854,6 +854,10 @@ class JobFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
854
854
|
"description": "icontains",
|
|
855
855
|
},
|
|
856
856
|
)
|
|
857
|
+
job_queues = NaturalKeyOrPKMultipleChoiceFilter(
|
|
858
|
+
queryset=JobQueue.objects.all(),
|
|
859
|
+
label="Job Queue (name or ID)",
|
|
860
|
+
)
|
|
857
861
|
|
|
858
862
|
class Meta:
|
|
859
863
|
model = Job
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -38,6 +38,7 @@ from nautobot.core.forms import (
|
|
|
38
38
|
)
|
|
39
39
|
from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
|
|
40
40
|
from nautobot.core.forms.forms import ConfirmationForm
|
|
41
|
+
from nautobot.core.forms.widgets import ClearableFileInput
|
|
41
42
|
from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
|
|
42
43
|
from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
43
44
|
from nautobot.extras.choices import (
|
|
@@ -985,6 +986,9 @@ class ImageAttachmentForm(BootstrapMixin, forms.ModelForm):
|
|
|
985
986
|
"name",
|
|
986
987
|
"image",
|
|
987
988
|
]
|
|
989
|
+
widgets = {
|
|
990
|
+
"image": ClearableFileInput,
|
|
991
|
+
}
|
|
988
992
|
|
|
989
993
|
|
|
990
994
|
#
|
nautobot/extras/jobs.py
CHANGED
|
@@ -37,6 +37,7 @@ from nautobot.core.forms import (
|
|
|
37
37
|
DynamicModelMultipleChoiceField,
|
|
38
38
|
JSONField,
|
|
39
39
|
)
|
|
40
|
+
from nautobot.core.forms.widgets import ClearableFileInput
|
|
40
41
|
from nautobot.core.utils.config import get_settings_or_config
|
|
41
42
|
from nautobot.core.utils.logging import sanitize
|
|
42
43
|
from nautobot.core.utils.lookup import get_model_from_name
|
|
@@ -1040,12 +1041,18 @@ class DatabaseFileField(forms.FileField):
|
|
|
1040
1041
|
widget = DBClearableFileInput
|
|
1041
1042
|
|
|
1042
1043
|
|
|
1044
|
+
class BootstrapStyleFileField(forms.FileField):
|
|
1045
|
+
"""File picker with UX bootstrap style and clearable checkbox."""
|
|
1046
|
+
|
|
1047
|
+
widget = ClearableFileInput
|
|
1048
|
+
|
|
1049
|
+
|
|
1043
1050
|
class FileVar(ScriptVariable):
|
|
1044
1051
|
"""
|
|
1045
1052
|
An uploaded file.
|
|
1046
1053
|
"""
|
|
1047
1054
|
|
|
1048
|
-
form_field =
|
|
1055
|
+
form_field = BootstrapStyleFileField
|
|
1049
1056
|
|
|
1050
1057
|
|
|
1051
1058
|
class IPAddressVar(ScriptVariable):
|
|
@@ -131,16 +131,20 @@ class GitRepository(PrimaryModel):
|
|
|
131
131
|
|
|
132
132
|
def get_latest_sync(self):
|
|
133
133
|
"""
|
|
134
|
-
Return a `JobResult` for the latest sync operation.
|
|
134
|
+
Return a `JobResult` for the latest sync operation if one has occurred.
|
|
135
135
|
|
|
136
136
|
Returns:
|
|
137
|
-
JobResult
|
|
137
|
+
Returns a `JobResult` if the repo has been synced before, otherwise returns None.
|
|
138
138
|
"""
|
|
139
139
|
from nautobot.extras.models import JobResult
|
|
140
140
|
|
|
141
141
|
# This will match all "GitRepository" jobs (pull/refresh, dry-run, etc.)
|
|
142
142
|
prefix = "nautobot.core.jobs.GitRepository"
|
|
143
|
-
|
|
143
|
+
|
|
144
|
+
if JobResult.objects.filter(task_name__startswith=prefix, task_kwargs__repository=self.pk).exists():
|
|
145
|
+
return JobResult.objects.filter(task_name__startswith=prefix, task_kwargs__repository=self.pk).latest()
|
|
146
|
+
else:
|
|
147
|
+
return None
|
|
144
148
|
|
|
145
149
|
def to_csv(self):
|
|
146
150
|
return (
|
|
@@ -728,6 +728,31 @@ def register_plugin_menu_items(section_name, menu_items):
|
|
|
728
728
|
#
|
|
729
729
|
|
|
730
730
|
|
|
731
|
+
class CustomValidatorContext(dict):
|
|
732
|
+
def __init__(self, obj):
|
|
733
|
+
"""
|
|
734
|
+
If there is an active change context, meaning we are in a web request context,
|
|
735
|
+
we have access to the current user object. Otherwise, we are likely running inside
|
|
736
|
+
a management command or other non-web or non-Job context, and we should use an AnonymousUser.
|
|
737
|
+
This ensures people's custom validators don't outright break when running in non-web
|
|
738
|
+
contexts, and should generally provide a sane default, given validation based on the
|
|
739
|
+
user is commonly going to be least-privelege based, and thus the AnonymousUser will
|
|
740
|
+
cause such validation logic to fail closed.
|
|
741
|
+
"""
|
|
742
|
+
from django.contrib.auth.models import AnonymousUser
|
|
743
|
+
|
|
744
|
+
from nautobot.extras.signals import change_context_state
|
|
745
|
+
|
|
746
|
+
change_context = change_context_state.get()
|
|
747
|
+
user = None
|
|
748
|
+
if change_context:
|
|
749
|
+
user = change_context.get_user()
|
|
750
|
+
if user is None:
|
|
751
|
+
user = AnonymousUser()
|
|
752
|
+
|
|
753
|
+
super().__init__(object=obj, user=user)
|
|
754
|
+
|
|
755
|
+
|
|
731
756
|
class CustomValidator:
|
|
732
757
|
"""
|
|
733
758
|
This class is used to register plugin custom model validators which act on specified models. It contains the clean
|
|
@@ -742,7 +767,7 @@ class CustomValidator:
|
|
|
742
767
|
model = None
|
|
743
768
|
|
|
744
769
|
def __init__(self, obj):
|
|
745
|
-
self.context =
|
|
770
|
+
self.context = CustomValidatorContext(obj)
|
|
746
771
|
|
|
747
772
|
def validation_error(self, message):
|
|
748
773
|
"""
|
|
@@ -12,45 +12,43 @@
|
|
|
12
12
|
<strong>Summary of Results</strong>
|
|
13
13
|
</div>
|
|
14
14
|
<table class="table table-hover panel-body">
|
|
15
|
-
{% if result.job_model is not None %}
|
|
16
15
|
<tr>
|
|
17
16
|
<td>Job Description</td>
|
|
18
|
-
<td>{{ result.job_model.description | render_markdown }}</td>
|
|
17
|
+
<td>{{ result.job_model.description | render_markdown | placeholder }}</td>
|
|
19
18
|
</tr>
|
|
20
|
-
{% endif %}
|
|
21
19
|
<tr>
|
|
22
20
|
<td>Status</td>
|
|
23
21
|
<td><span id="pending-result-label">{% include 'extras/inc/job_label.html' with result=result %}</span></td>
|
|
24
22
|
</tr>
|
|
25
23
|
<tr>
|
|
26
24
|
<td>Started at</td>
|
|
27
|
-
<td>{{ result.date_created }}</td>
|
|
25
|
+
<td>{{ result.date_created | placeholder }}</td>
|
|
28
26
|
</tr>
|
|
29
27
|
<tr>
|
|
30
28
|
<td>User</td>
|
|
31
|
-
<td>{{ result.user }}</td>
|
|
29
|
+
<td>{{ result.user | placeholder }}</td>
|
|
32
30
|
</tr>
|
|
33
31
|
<tr>
|
|
34
32
|
<td>Duration</td>
|
|
35
33
|
<td>
|
|
36
|
-
{% if result.date_done %}
|
|
37
|
-
{{ result.duration }}
|
|
38
|
-
{% else %}
|
|
34
|
+
{% if result.date_created and not result.date_done %}
|
|
39
35
|
<img src="{% static 'img/ajax-loader.gif' %}">
|
|
36
|
+
{% else %}
|
|
37
|
+
{{ result.duration | placeholder}}
|
|
40
38
|
{% endif %}
|
|
41
39
|
</td>
|
|
42
40
|
</tr>
|
|
43
41
|
<tr>
|
|
44
42
|
<td>Return Value</td>
|
|
45
43
|
<td>
|
|
46
|
-
{% if result.date_done %}
|
|
44
|
+
{% if result.date_created and not result.date_done %}
|
|
45
|
+
<img src="{% static 'img/ajax-loader.gif' %}">
|
|
46
|
+
{% else %}
|
|
47
47
|
{% if result.result %}
|
|
48
48
|
<pre>{{ result.result | render_json }}</pre>
|
|
49
49
|
{% else %}
|
|
50
50
|
{{ result.result | placeholder }}
|
|
51
51
|
{% endif %}
|
|
52
|
-
{% else %}
|
|
53
|
-
<img src="{% static 'img/ajax-loader.gif' %}">
|
|
54
52
|
{% endif %}
|
|
55
53
|
</td>
|
|
56
54
|
</tr>
|
|
@@ -89,6 +87,7 @@
|
|
|
89
87
|
<input class="form-control" id="log-filter" type="text" placeholder="Filter log level or message" title="Filter log level or message" style="height: 23px" />
|
|
90
88
|
</div>
|
|
91
89
|
</div>
|
|
92
|
-
{%
|
|
90
|
+
{% if result and result.pk %}
|
|
91
|
+
{% ajax_table "log_table" "extras:jobresult_log-table" pk=result.pk %}
|
|
92
|
+
{% endif %}
|
|
93
93
|
</div>
|
|
94
|
-
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{% extends 'generic/object_retrieve.html' %}
|
|
2
2
|
{% load helpers %}
|
|
3
|
+
{% load static %}
|
|
3
4
|
|
|
4
5
|
{% block title %}{{ object }}{% endblock %}
|
|
5
6
|
|
|
@@ -95,6 +96,20 @@
|
|
|
95
96
|
</tr>
|
|
96
97
|
</table>
|
|
97
98
|
</div>
|
|
99
|
+
<div class="panel panel-default">
|
|
100
|
+
<div class="panel-heading">
|
|
101
|
+
<strong>Object Data</strong>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="panel-body">
|
|
104
|
+
<div class="editor-container"
|
|
105
|
+
data-lang="json"
|
|
106
|
+
data-value="{{ object.object_data|render_json:False }}"
|
|
107
|
+
style="max-height: 300px">
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="col-md-7">
|
|
98
113
|
<div class="panel panel-default">
|
|
99
114
|
<div class="panel-heading">
|
|
100
115
|
<strong>Difference</strong>
|
|
@@ -119,22 +134,17 @@
|
|
|
119
134
|
{% endif %}
|
|
120
135
|
</span>
|
|
121
136
|
{% else %}
|
|
122
|
-
<
|
|
123
|
-
|
|
137
|
+
<div class="editor-container"
|
|
138
|
+
data-mode="diff"
|
|
139
|
+
data-original="{{ diff_removed | render_json:False }}"
|
|
140
|
+
data-modified="{{ diff_added | render_json:False }}"
|
|
141
|
+
data-lang="json"
|
|
142
|
+
style="max-height: 730px">
|
|
143
|
+
</div>
|
|
124
144
|
{% endif %}
|
|
125
145
|
</div>
|
|
126
146
|
</div>
|
|
127
147
|
</div>
|
|
128
|
-
<div class="col-md-7">
|
|
129
|
-
<div class="panel panel-default">
|
|
130
|
-
<div class="panel-heading">
|
|
131
|
-
<strong>Object Data</strong>
|
|
132
|
-
</div>
|
|
133
|
-
<div class="panel-body">
|
|
134
|
-
<pre>{{ object.object_data|render_json }}</pre>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
148
|
</div>
|
|
139
149
|
<div class="row">
|
|
140
150
|
<div class="col-md-12">
|
|
@@ -147,3 +157,9 @@
|
|
|
147
157
|
</div>
|
|
148
158
|
</div>
|
|
149
159
|
{% endblock %}
|
|
160
|
+
|
|
161
|
+
{% block javascript %}
|
|
162
|
+
{{ block.super }}
|
|
163
|
+
<script src="{% static 'js/editor.js' %}"></script>
|
|
164
|
+
{% endblock %}
|
|
165
|
+
|
|
@@ -196,20 +196,6 @@ class ComputedFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
196
196
|
|
|
197
197
|
class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
|
198
198
|
model = ConfigContext
|
|
199
|
-
create_data = [
|
|
200
|
-
{
|
|
201
|
-
"name": "Config Context 4",
|
|
202
|
-
"data": {"more_foo": True},
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
"name": "Config Context 5",
|
|
206
|
-
"data": {"more_bar": False},
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
"name": "Config Context 6",
|
|
210
|
-
"data": {"more_baz": None},
|
|
211
|
-
},
|
|
212
|
-
]
|
|
213
199
|
bulk_update_data = {
|
|
214
200
|
"description": "New description",
|
|
215
201
|
}
|
|
@@ -220,6 +206,21 @@ class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
|
|
220
206
|
ConfigContext.objects.create(name="Config Context 1", weight=100, data={"foo": 123})
|
|
221
207
|
ConfigContext.objects.create(name="Config Context 2", weight=200, data={"bar": 456})
|
|
222
208
|
ConfigContext.objects.create(name="Config Context 3", weight=300, data={"baz": 789})
|
|
209
|
+
cls.create_data = [
|
|
210
|
+
{
|
|
211
|
+
"name": "Config Context 4",
|
|
212
|
+
"data": {"more_foo": True},
|
|
213
|
+
"tags": [tag.pk for tag in Tag.objects.get_for_model(Device)],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "Config Context 5",
|
|
217
|
+
"data": {"more_bar": False},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "Config Context 6",
|
|
221
|
+
"data": {"more_baz": None},
|
|
222
|
+
},
|
|
223
|
+
]
|
|
223
224
|
|
|
224
225
|
def test_render_configcontext_for_object(self):
|
|
225
226
|
"""
|
|
@@ -1797,7 +1798,7 @@ class JobTest(
|
|
|
1797
1798
|
)
|
|
1798
1799
|
self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
|
|
1799
1800
|
self.assertIn(
|
|
1800
|
-
"task_queue and job_queue are both specified. Please
|
|
1801
|
+
"task_queue and job_queue are both specified. Please specify only one or another.", str(response.content)
|
|
1801
1802
|
)
|
|
1802
1803
|
|
|
1803
1804
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
@@ -667,6 +667,20 @@ class DynamicGroupModelTest(DynamicGroupTestBase): # TODO: BaseModelTestCase mi
|
|
|
667
667
|
# Cleanup because we're using class-based fixtures in `setUpTestData()`
|
|
668
668
|
group.refresh_from_db()
|
|
669
669
|
|
|
670
|
+
def test_set_filter_on_ipaddress_dynamic_group(self):
|
|
671
|
+
"""
|
|
672
|
+
Test `DynamicGroup.set_filter()` for an IPAddress Dynamic Group.
|
|
673
|
+
https://github.com/nautobot/nautobot/issues/6805
|
|
674
|
+
"""
|
|
675
|
+
ipaddress_dg = DynamicGroup.objects.create(
|
|
676
|
+
name="IP Address Dynamic Group",
|
|
677
|
+
content_type=ContentType.objects.get_for_model(IPAddress),
|
|
678
|
+
description="IP Address Dynamic Group",
|
|
679
|
+
)
|
|
680
|
+
# Test the fact that set_filter correctly discard an empty PrefixQuerySet
|
|
681
|
+
ipaddress_dg.set_filter({"parent": Prefix.objects.none()})
|
|
682
|
+
self.assertEqual(ipaddress_dg.filter, {})
|
|
683
|
+
|
|
670
684
|
def test_add_child(self):
|
|
671
685
|
"""Test `DynamicGroup.add_child()`."""
|
|
672
686
|
self.parent.add_child(
|
|
@@ -880,6 +880,8 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
880
880
|
generic_filter_tests = (
|
|
881
881
|
("grouping",),
|
|
882
882
|
("job_class_name",),
|
|
883
|
+
("job_queues", "job_queues__id"),
|
|
884
|
+
("job_queues", "job_queues__name"),
|
|
883
885
|
("module_name",),
|
|
884
886
|
("name",),
|
|
885
887
|
)
|
|
@@ -17,11 +17,12 @@ from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Man
|
|
|
17
17
|
from nautobot.dcim.tests.test_views import create_test_device
|
|
18
18
|
from nautobot.extras import plugins
|
|
19
19
|
from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoices
|
|
20
|
+
from nautobot.extras.context_managers import web_request_context
|
|
20
21
|
from nautobot.extras.jobs import get_job
|
|
21
22
|
from nautobot.extras.models import CustomField, Relationship, RelationshipAssociation, Role, Secret, Status
|
|
22
23
|
from nautobot.extras.plugins.exceptions import PluginImproperlyConfigured
|
|
23
24
|
from nautobot.extras.plugins.utils import load_plugin
|
|
24
|
-
from nautobot.extras.plugins.validators import wrap_model_clean_methods
|
|
25
|
+
from nautobot.extras.plugins.validators import CustomValidator, wrap_model_clean_methods
|
|
25
26
|
from nautobot.extras.plugins.views import extract_app_data
|
|
26
27
|
from nautobot.extras.registry import DatasourceContent, registry
|
|
27
28
|
from nautobot.ipam.models import IPAddress, Namespace, Prefix
|
|
@@ -478,6 +479,16 @@ class AppAPITest(APIViewTestCases.APIViewTestCase):
|
|
|
478
479
|
pass
|
|
479
480
|
|
|
480
481
|
|
|
482
|
+
class TestUserContextCustomValidator(CustomValidator):
|
|
483
|
+
model = "dcim.locationtype"
|
|
484
|
+
|
|
485
|
+
def clean(self):
|
|
486
|
+
"""
|
|
487
|
+
Used to validate that the correct user context is available in the custom validator.
|
|
488
|
+
"""
|
|
489
|
+
self.validation_error(self.context["user"])
|
|
490
|
+
|
|
491
|
+
|
|
481
492
|
class AppCustomValidationTest(TestCase):
|
|
482
493
|
def setUp(self):
|
|
483
494
|
# When creating a fresh test DB, wrapping model clean methods fails, which is normal.
|
|
@@ -485,6 +496,7 @@ class AppCustomValidationTest(TestCase):
|
|
|
485
496
|
# must manually call the method again to actually perform the action, now that the
|
|
486
497
|
# ContentType table has been created.
|
|
487
498
|
wrap_model_clean_methods()
|
|
499
|
+
super().setUp()
|
|
488
500
|
|
|
489
501
|
def test_custom_validator_raises_exception(self):
|
|
490
502
|
location_type = LocationType.objects.get(name="Campus")
|
|
@@ -513,6 +525,25 @@ class AppCustomValidationTest(TestCase):
|
|
|
513
525
|
with self.assertRaises(ValidationError):
|
|
514
526
|
relationship_assoc.clean()
|
|
515
527
|
|
|
528
|
+
def test_custom_validator_non_web_request_uses_anonymous_user(self):
|
|
529
|
+
location_type = LocationType.objects.get(name="Campus")
|
|
530
|
+
registry["plugin_custom_validators"]["dcim.locationtype"] = [TestUserContextCustomValidator]
|
|
531
|
+
|
|
532
|
+
from django.contrib.auth.models import AnonymousUser
|
|
533
|
+
|
|
534
|
+
with self.assertRaises(ValidationError) as context:
|
|
535
|
+
location_type.clean()
|
|
536
|
+
self.assertEqual(context.exception.message, AnonymousUser())
|
|
537
|
+
|
|
538
|
+
def test_custom_validator_web_request_uses_real_user(self):
|
|
539
|
+
location_type = LocationType.objects.get(name="Campus")
|
|
540
|
+
registry["plugin_custom_validators"]["dcim.locationtype"] = [TestUserContextCustomValidator]
|
|
541
|
+
|
|
542
|
+
with self.assertRaises(ValidationError) as context:
|
|
543
|
+
with web_request_context(user=self.user):
|
|
544
|
+
location_type.clean()
|
|
545
|
+
self.assertEqual(context.exception.message, self.user)
|
|
546
|
+
|
|
516
547
|
|
|
517
548
|
class ExampleModelCustomActionViewTest(TestCase):
|
|
518
549
|
"""Test for custom action view `all_names` added to Example App"""
|