nautobot 2.4.1__py3-none-any.whl → 2.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +1 -1
- nautobot/circuits/tests/integration/test_circuit.py +135 -0
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
- nautobot/circuits/tests/integration/test_relationships.py +1 -1
- nautobot/circuits/views.py +4 -1
- nautobot/cloud/api/views.py +3 -3
- nautobot/core/apps/__init__.py +0 -5
- 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/management/commands/generate_performance_test_endpoints.py +268 -0
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_destroy.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 +2 -0
- nautobot/core/templates/widgets/clearable_file.html +5 -0
- nautobot/core/templatetags/helpers.py +3 -3
- nautobot/core/testing/integration.py +469 -12
- nautobot/core/tests/test_commands.py +31 -0
- nautobot/core/tests/test_jobs.py +34 -2
- nautobot/core/tests/test_utils.py +17 -2
- nautobot/core/utils/git.py +7 -2
- nautobot/core/utils/lookup.py +12 -1
- nautobot/core/views/generic.py +10 -2
- nautobot/core/views/mixins.py +22 -7
- nautobot/core/views/utils.py +2 -2
- nautobot/dcim/api/views.py +11 -10
- nautobot/dcim/forms.py +15 -6
- nautobot/dcim/models/devices.py +1 -2
- nautobot/dcim/tables/devices.py +2 -1
- nautobot/dcim/templates/dcim/cable.html +1 -1
- 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/base.html +1 -1
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +3 -3
- nautobot/dcim/templates/dcim/device.html +2 -2
- nautobot/dcim/templates/dcim/device_component.html +1 -1
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/frontport.html +7 -2
- nautobot/dcim/templates/dcim/interface.html +9 -4
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/locationtype.html +1 -1
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/manufacturer.html +1 -1
- nautobot/dcim/templates/dcim/platform.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed.html +9 -4
- nautobot/dcim/templates/dcim/poweroutlet.html +14 -4
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +14 -4
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rackgroup.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation.html +2 -2
- nautobot/dcim/templates/dcim/rearport.html +7 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
- nautobot/dcim/tests/integration/test_fileinputpicker.py +87 -0
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
- nautobot/dcim/tests/test_models.py +1 -1
- nautobot/dcim/tests/test_views.py +9 -1
- nautobot/dcim/views.py +12 -15
- nautobot/extras/api/serializers.py +33 -0
- nautobot/extras/api/views.py +13 -5
- nautobot/extras/constants.py +1 -0
- nautobot/extras/datasources/git.py +125 -0
- nautobot/extras/forms/forms.py +4 -0
- nautobot/extras/jobs.py +8 -1
- nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
- nautobot/extras/models/customfields.py +29 -12
- nautobot/extras/models/datasources.py +85 -0
- nautobot/extras/models/models.py +15 -0
- nautobot/extras/models/relationships.py +17 -5
- nautobot/extras/signals.py +15 -1
- nautobot/extras/templates/extras/computedfield.html +1 -1
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/customfield.html +1 -1
- nautobot/extras/templates/extras/customlink.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/exporttemplate.html +1 -1
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/graphqlquery.html +1 -1
- nautobot/extras/templates/extras/job.html +1 -0
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
- nautobot/extras/templates/extras/jobhook.html +1 -1
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/plugin_detail.html +1 -1
- nautobot/extras/templates/extras/relationship.html +1 -63
- nautobot/extras/templates/extras/role_retrieve.html +1 -1
- nautobot/extras/templates/extras/scheduledjob.html +1 -1
- nautobot/extras/templates/extras/secret.html +1 -1
- nautobot/extras/templates/extras/secretsgroup.html +1 -1
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/webhook.html +1 -1
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
- nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
- nautobot/extras/tests/git_helper.py +9 -1
- nautobot/extras/tests/integration/__init__.py +29 -16
- nautobot/extras/tests/test_api.py +6 -0
- nautobot/extras/tests/test_customfields.py +49 -51
- nautobot/extras/tests/test_datasources.py +27 -0
- nautobot/extras/tests/test_dynamicgroups.py +14 -0
- nautobot/extras/tests/test_models.py +283 -0
- nautobot/extras/tests/test_utils.py +22 -1
- nautobot/extras/tests/test_views.py +197 -9
- nautobot/extras/utils.py +47 -8
- nautobot/extras/views.py +84 -26
- nautobot/ipam/api/views.py +3 -3
- nautobot/ipam/forms.py +2 -6
- nautobot/ipam/models.py +8 -2
- nautobot/ipam/tables.py +2 -2
- nautobot/ipam/templates/ipam/ipaddress.html +1 -1
- nautobot/ipam/templates/ipam/prefix.html +1 -1
- nautobot/ipam/templates/ipam/rir.html +1 -1
- nautobot/ipam/templates/ipam/routetarget.html +1 -1
- nautobot/ipam/templates/ipam/service.html +1 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlangroup.html +1 -1
- nautobot/ipam/templates/ipam/vrf.html +1 -1
- nautobot/ipam/tests/test_models.py +24 -0
- nautobot/ipam/tests/test_utils.py +41 -2
- nautobot/ipam/utils/__init__.py +18 -11
- nautobot/project-static/bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js +11 -0
- nautobot/project-static/docs/404.html +87 -12
- nautobot/project-static/docs/apps/index.html +88 -13
- nautobot/project-static/docs/apps/nautobot-apps.html +88 -13
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
- nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -21
- nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +89 -14
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
- nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
- nautobot/project-static/docs/development/apps/api/setup.html +88 -13
- nautobot/project-static/docs/development/apps/api/testing.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
- nautobot/project-static/docs/development/apps/index.html +87 -12
- nautobot/project-static/docs/development/apps/migration/code-updates.html +93 -17
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +89 -14
- nautobot/project-static/docs/development/apps/migration/from-v1.html +90 -15
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
- nautobot/project-static/docs/development/core/application-registry.html +87 -12
- nautobot/project-static/docs/development/core/best-practices.html +88 -13
- nautobot/project-static/docs/development/core/bootstrap-ui.html +88 -13
- nautobot/project-static/docs/development/core/caching.html +87 -12
- nautobot/project-static/docs/development/core/controllers.html +87 -12
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +94 -19
- nautobot/project-static/docs/development/core/generic-views.html +87 -12
- nautobot/project-static/docs/development/core/getting-started.html +89 -14
- nautobot/project-static/docs/development/core/homepage.html +87 -12
- nautobot/project-static/docs/development/core/index.html +88 -13
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +90 -15
- nautobot/project-static/docs/development/core/model-checklist.html +88 -13
- nautobot/project-static/docs/development/core/model-features.html +87 -12
- nautobot/project-static/docs/development/core/natural-keys.html +87 -12
- nautobot/project-static/docs/development/core/navigation-menu.html +88 -13
- nautobot/project-static/docs/development/core/release-checklist.html +88 -13
- nautobot/project-static/docs/development/core/role-internals.html +87 -12
- nautobot/project-static/docs/development/core/settings.html +88 -13
- nautobot/project-static/docs/development/core/style-guide.html +91 -16
- nautobot/project-static/docs/development/core/templates.html +88 -13
- nautobot/project-static/docs/development/core/testing.html +87 -12
- nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
- nautobot/project-static/docs/development/core/user-preferences.html +87 -12
- nautobot/project-static/docs/development/index.html +87 -12
- nautobot/project-static/docs/development/jobs/index.html +95 -13
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +90 -14
- nautobot/project-static/docs/index.html +90 -14
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +89 -14
- nautobot/project-static/docs/overview/design_philosophy.html +87 -12
- nautobot/project-static/docs/release-notes/index.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.0.html +89 -14
- nautobot/project-static/docs/release-notes/version-1.1.html +89 -14
- nautobot/project-static/docs/release-notes/version-1.2.html +90 -15
- nautobot/project-static/docs/release-notes/version-1.3.html +88 -13
- nautobot/project-static/docs/release-notes/version-1.4.html +104 -29
- nautobot/project-static/docs/release-notes/version-1.5.html +95 -20
- nautobot/project-static/docs/release-notes/version-1.6.html +91 -16
- nautobot/project-static/docs/release-notes/version-2.0.html +97 -22
- nautobot/project-static/docs/release-notes/version-2.1.html +94 -19
- nautobot/project-static/docs/release-notes/version-2.2.html +88 -13
- nautobot/project-static/docs/release-notes/version-2.3.html +91 -16
- nautobot/project-static/docs/release-notes/version-2.4.html +465 -12
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +296 -288
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +90 -15
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +91 -16
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +88 -13
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +90 -15
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +95 -20
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +90 -15
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +88 -13
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +91 -16
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +102 -27
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +89 -14
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +88 -13
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +88 -13
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +93 -18
- nautobot/project-static/docs/user-guide/administration/installation/services.html +88 -13
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
- nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
- nautobot/project-static/docs/user-guide/administration/security/notices.html +9844 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +88 -13
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +98 -20
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +99 -24
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +188 -30
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
- nautobot/project-static/docs/user-guide/index.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +88 -13
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +90 -15
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +89 -14
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +93 -18
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
- nautobot/project-static/js/dropdown.js +28 -0
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/templates/tenancy/tenant.html +1 -2
- 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_views.py +5 -1
- nautobot/tenancy/urls.py +7 -79
- nautobot/tenancy/views.py +51 -80
- nautobot/virtualization/templates/virtualization/cluster.html +1 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
- nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
- nautobot/wireless/api/serializers.py +6 -1
- nautobot/wireless/api/views.py +3 -3
- nautobot/wireless/tests/test_api.py +5 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/METADATA +12 -12
- {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/RECORD +459 -443
- nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
- nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
- /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/NOTICE +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/WHEEL +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,63 +1 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
2
|
-
{% load buttons %}
|
|
3
|
-
{% load plugins %}
|
|
4
|
-
{% load perms %}
|
|
5
|
-
{% load helpers %}
|
|
6
|
-
|
|
7
|
-
{% block content_left_page %}
|
|
8
|
-
<div class="panel panel-default">
|
|
9
|
-
<div class="panel-heading">
|
|
10
|
-
<strong>Relationship</strong>
|
|
11
|
-
</div>
|
|
12
|
-
<table class="table table-hover panel-body attr-table">
|
|
13
|
-
<tr>
|
|
14
|
-
<td>Description</td>
|
|
15
|
-
<td>{{ object.description | placeholder }}</td>
|
|
16
|
-
</tr>
|
|
17
|
-
<tr>
|
|
18
|
-
<td>Type</td>
|
|
19
|
-
<td>{{ object.type }}</td>
|
|
20
|
-
</tr>
|
|
21
|
-
<tr>
|
|
22
|
-
<td>Required On</td>
|
|
23
|
-
<td>{{ object.get_required_on_display }}</td>
|
|
24
|
-
</tr>
|
|
25
|
-
<tr>
|
|
26
|
-
<td>On Advanced Tab</td>
|
|
27
|
-
<td>{{ object.advanced_ui | render_boolean }}</td>
|
|
28
|
-
</tr>
|
|
29
|
-
<tr>
|
|
30
|
-
<td>Source Content Type</td>
|
|
31
|
-
<td>{{ object.source_type }}</td>
|
|
32
|
-
</tr>
|
|
33
|
-
<tr>
|
|
34
|
-
<td>Source Label</td>
|
|
35
|
-
<td>{{ object.source_label | placeholder }}</td>
|
|
36
|
-
</tr>
|
|
37
|
-
<tr>
|
|
38
|
-
<td>Source Filter</td>
|
|
39
|
-
<td>{% if object.source_filter %}<pre>{{ object.source_filter | render_json }}</pre>{% else %}{{ None | placeholder }}{% endif %}</td>
|
|
40
|
-
</tr>
|
|
41
|
-
<tr>
|
|
42
|
-
<td>Hide on Source Object</td>
|
|
43
|
-
<td>{{ object.source_hidden | render_boolean }}</td>
|
|
44
|
-
</tr>
|
|
45
|
-
<tr>
|
|
46
|
-
<td>Destination Content Type</td>
|
|
47
|
-
<td>{{ object.destination_type }}</td>
|
|
48
|
-
</tr>
|
|
49
|
-
<tr>
|
|
50
|
-
<td>Destination Label</td>
|
|
51
|
-
<td>{{ object.destination_label | placeholder }}</td>
|
|
52
|
-
</tr>
|
|
53
|
-
<tr>
|
|
54
|
-
<td>Destination Filter</td>
|
|
55
|
-
<td>{% if object.destination_filter %}<pre>{{ object.destination_filter | render_json }}</pre>{% else %}{{ None | placeholder }}{% endif %}</td>
|
|
56
|
-
</tr>
|
|
57
|
-
<tr>
|
|
58
|
-
<td>Hide on Destination Object</td>
|
|
59
|
-
<td>{{ object.destination_hidden | render_boolean }}</td>
|
|
60
|
-
</tr>
|
|
61
|
-
</table>
|
|
62
|
-
</div>
|
|
63
|
-
{% endblock content_left_page %}
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
SOURCE_DIR = os.path.join(os.path.dirname(__file__), "git_data")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def create_and_populate_git_repository(target_path):
|
|
17
|
+
def create_and_populate_git_repository(target_path, divergent_branch=None):
|
|
18
18
|
"""
|
|
19
19
|
Create a Git repository in `target_path` and populate it with commits and tags based on contents of `SOURCE_DIR`.
|
|
20
20
|
|
|
@@ -40,6 +40,9 @@ def create_and_populate_git_repository(target_path):
|
|
|
40
40
|
Note that each commit is fully defined by the files in the appropriate subdirectory; if you want a file to exist
|
|
41
41
|
across multiple separate commits, it must exist in multiple subdirectories. Use of symlinks is encouraged in such
|
|
42
42
|
a scenario.
|
|
43
|
+
|
|
44
|
+
You can optionally create and check out a divergent branch from the main branch by passing a branch name as the `divergent_branch`.
|
|
45
|
+
This will write a commit to the divergent branch and tag it with the branch name with the `-tag` suffix.
|
|
43
46
|
"""
|
|
44
47
|
os.makedirs(target_path, exist_ok=True)
|
|
45
48
|
repo = Repo.init(target_path, initial_branch="main")
|
|
@@ -69,6 +72,11 @@ def create_and_populate_git_repository(target_path):
|
|
|
69
72
|
# Directory "01-valid-files" --> tag "valid-files" so that we won't break tests if we renumber the directories
|
|
70
73
|
repo.create_tag(dirname[3:], message=f"Tag based on {dirname} files")
|
|
71
74
|
|
|
75
|
+
if divergent_branch:
|
|
76
|
+
repo.create_head(divergent_branch)
|
|
77
|
+
repo.index.commit("divergent-branch")
|
|
78
|
+
repo.create_tag(f"{divergent_branch}-tag", message=f"Tag for divergent branch {divergent_branch}")
|
|
79
|
+
|
|
72
80
|
|
|
73
81
|
if __name__ == "__main__":
|
|
74
82
|
directory_path = tempfile.TemporaryDirectory().name # pylint: disable=consider-using-with
|
|
@@ -6,28 +6,41 @@ from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Man
|
|
|
6
6
|
from nautobot.extras.models import Role, Status
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def create_test_device():
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
def create_test_device(name=None, location_name=None, test_uuid=None):
|
|
10
|
+
if not test_uuid:
|
|
11
|
+
test_uuid = str(uuid.uuid4())
|
|
12
|
+
if not name:
|
|
13
|
+
name = f"Test Device {test_uuid}"
|
|
14
|
+
if not location_name:
|
|
15
|
+
location_name = f"Test Location {test_uuid}"
|
|
16
|
+
|
|
17
|
+
location_type, location_type_created = LocationType.objects.get_or_create(name=f"Test Location Type {test_uuid}")
|
|
18
|
+
if location_type_created:
|
|
19
|
+
location_type.content_types.add(ContentType.objects.get_for_model(Device))
|
|
20
|
+
location_type.save()
|
|
21
|
+
|
|
20
22
|
location_status = Status.objects.get_for_model(Location).first()
|
|
21
|
-
location = Location.objects.
|
|
22
|
-
name=
|
|
23
|
+
location, _ = Location.objects.get_or_create(
|
|
24
|
+
name=location_name,
|
|
23
25
|
status=location_status,
|
|
24
26
|
location_type=location_type,
|
|
25
27
|
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
device_role, device_role_created = Role.objects.get_or_create(name="Device Role")
|
|
30
|
+
if device_role_created:
|
|
31
|
+
device_role.content_types.add(ContentType.objects.get_for_model(Device))
|
|
32
|
+
device_role.save()
|
|
33
|
+
|
|
34
|
+
manufacturer, _ = Manufacturer.objects.get_or_create(
|
|
35
|
+
name=f"Test Manufacturer {test_uuid}",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
device_type, _ = DeviceType.objects.get_or_create(manufacturer=manufacturer, model=f"Test Model {test_uuid}")
|
|
39
|
+
|
|
40
|
+
return Device.objects.create(
|
|
41
|
+
name=name,
|
|
28
42
|
role=device_role,
|
|
29
43
|
device_type=device_type,
|
|
30
44
|
location=location,
|
|
31
45
|
status=location_status,
|
|
32
46
|
)
|
|
33
|
-
return device
|
|
@@ -1207,6 +1207,10 @@ class GitRepositoryTest(APIViewTestCases.APIViewTestCase):
|
|
|
1207
1207
|
url = reverse("extras-api:gitrepository-sync", kwargs={"pk": self.repos[0].id})
|
|
1208
1208
|
response = self.client.post(url, format="json", **self.header)
|
|
1209
1209
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1210
|
+
self.assertIn("message", response.data)
|
|
1211
|
+
self.assertIn("job_result", response.data)
|
|
1212
|
+
self.assertEqual(response.data["message"], f"Repository {self.repos[0].name} sync job added to queue.")
|
|
1213
|
+
self.assertIsInstance(response.data["job_result"], dict)
|
|
1210
1214
|
|
|
1211
1215
|
def test_create_with_app_provided_contents(self):
|
|
1212
1216
|
"""Test that `provided_contents` published by an App works."""
|
|
@@ -1241,6 +1245,8 @@ class GraphQLQueryTest(APIViewTestCases.APIViewTestCase):
|
|
|
1241
1245
|
},
|
|
1242
1246
|
]
|
|
1243
1247
|
|
|
1248
|
+
choices_fields = ["owner_content_type"]
|
|
1249
|
+
|
|
1244
1250
|
@classmethod
|
|
1245
1251
|
def setUpTestData(cls):
|
|
1246
1252
|
cls.graphqlqueries = (
|
|
@@ -53,12 +53,12 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
53
53
|
|
|
54
54
|
instance.refresh_from_db()
|
|
55
55
|
instance.key = "custom_field_2"
|
|
56
|
-
with self.
|
|
56
|
+
with self.assertRaisesRegex(ValidationError, "Key cannot be changed once created"):
|
|
57
57
|
instance.validated_save()
|
|
58
58
|
|
|
59
59
|
instance.refresh_from_db()
|
|
60
60
|
instance.type = CustomFieldTypeChoices.TYPE_SELECT
|
|
61
|
-
with self.
|
|
61
|
+
with self.assertRaisesRegex(ValidationError, "Type cannot be changed once created"):
|
|
62
62
|
instance.validated_save()
|
|
63
63
|
|
|
64
64
|
def test_simple_fields(self):
|
|
@@ -165,9 +165,14 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
165
165
|
cf.save()
|
|
166
166
|
cf.content_types.set([obj_type])
|
|
167
167
|
|
|
168
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option A")
|
|
169
|
-
|
|
170
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option
|
|
168
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option A", weight=100)
|
|
169
|
+
self.assertEqual(["Option A"], cf.choices)
|
|
170
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option B", weight=200)
|
|
171
|
+
self.assertEqual(["Option A", "Option B"], cf.choices)
|
|
172
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option C", weight=300)
|
|
173
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
174
|
+
with self.assertNumQueries(0): # verify caching
|
|
175
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
171
176
|
|
|
172
177
|
# Assign a value to the first Location
|
|
173
178
|
location = Location.objects.get(name="Location A")
|
|
@@ -199,9 +204,14 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
199
204
|
cf.save()
|
|
200
205
|
cf.content_types.set([obj_type])
|
|
201
206
|
|
|
202
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option A")
|
|
203
|
-
|
|
204
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option
|
|
207
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option A", weight=100)
|
|
208
|
+
self.assertEqual(["Option A"], cf.choices)
|
|
209
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option B", weight=200)
|
|
210
|
+
self.assertEqual(["Option A", "Option B"], cf.choices)
|
|
211
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option C", weight=300)
|
|
212
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
213
|
+
with self.assertNumQueries(0): # verify caching
|
|
214
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
205
215
|
|
|
206
216
|
# Assign a value to the first Location
|
|
207
217
|
location = Location.objects.get(name="Location A")
|
|
@@ -293,21 +303,18 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
293
303
|
# Assign a disallowed value (list) to the first Location
|
|
294
304
|
location = Location.objects.get(name="Location A")
|
|
295
305
|
location.cf[cf.key] = ["I", "am", "a", "list"]
|
|
296
|
-
with self.
|
|
306
|
+
with self.assertRaisesRegex(ValidationError, "Value must be a string"):
|
|
297
307
|
location.validated_save()
|
|
298
|
-
self.assertIn("Value must be a string", str(context.exception))
|
|
299
308
|
|
|
300
309
|
# Assign another disallowed value (int) to the first Location
|
|
301
310
|
location.cf[cf.key] = 2
|
|
302
|
-
with self.
|
|
311
|
+
with self.assertRaisesRegex(ValidationError, "Value must be a string"):
|
|
303
312
|
location.validated_save()
|
|
304
|
-
self.assertIn("Value must be a string", str(context.exception))
|
|
305
313
|
|
|
306
314
|
# Assign another disallowed value (bool) to the first Location
|
|
307
315
|
location.cf[cf.key] = True
|
|
308
|
-
with self.
|
|
316
|
+
with self.assertRaisesRegex(ValidationError, "Value must be a string"):
|
|
309
317
|
location.validated_save()
|
|
310
|
-
self.assertIn("Value must be a string", str(context.exception))
|
|
311
318
|
|
|
312
319
|
# Delete the stored value
|
|
313
320
|
location.cf.pop(cf.key)
|
|
@@ -1294,7 +1301,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1294
1301
|
custom_field.content_types.set([ContentType.objects.get_for_model(Provider)])
|
|
1295
1302
|
|
|
1296
1303
|
provider = Provider.objects.create(name="Test")
|
|
1297
|
-
with self.
|
|
1304
|
+
with self.assertRaisesRegex(ValidationError, "Missing required custom field 'custom_field'"):
|
|
1298
1305
|
provider.validated_save()
|
|
1299
1306
|
|
|
1300
1307
|
def test_custom_field_required_on_update(self):
|
|
@@ -1312,7 +1319,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1312
1319
|
provider = Provider.objects.create(name="Test", _custom_field_data={"custom_field": "Value"})
|
|
1313
1320
|
provider.validated_save()
|
|
1314
1321
|
provider._custom_field_data.pop("custom_field")
|
|
1315
|
-
with self.
|
|
1322
|
+
with self.assertRaisesRegex(ValidationError, "Missing required custom field 'custom_field'"):
|
|
1316
1323
|
provider.validated_save()
|
|
1317
1324
|
|
|
1318
1325
|
def test_update_removed_custom_field(self):
|
|
@@ -1373,7 +1380,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1373
1380
|
"""
|
|
1374
1381
|
Check that a ValidationError is raised if any required custom fields are not present.
|
|
1375
1382
|
"""
|
|
1376
|
-
cf3 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Baz", required=True)
|
|
1383
|
+
cf3 = CustomField(key="baz", type=CustomFieldTypeChoices.TYPE_TEXT, label="Baz", required=True)
|
|
1377
1384
|
cf3.save()
|
|
1378
1385
|
cf3.content_types.set([ContentType.objects.get_for_model(Location)])
|
|
1379
1386
|
|
|
@@ -1381,7 +1388,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1381
1388
|
|
|
1382
1389
|
# Set custom field data with a required field omitted
|
|
1383
1390
|
location.cf["foo"] = "abc"
|
|
1384
|
-
with self.
|
|
1391
|
+
with self.assertRaisesRegex(ValidationError, "Missing required custom field 'baz'"):
|
|
1385
1392
|
location.clean()
|
|
1386
1393
|
|
|
1387
1394
|
location.cf["baz"] = "def"
|
|
@@ -1427,38 +1434,20 @@ class CustomFieldModelTest(TestCase):
|
|
|
1427
1434
|
"""
|
|
1428
1435
|
Check the GraphQL validation method on CustomField Key Attribute.
|
|
1429
1436
|
"""
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
"
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
str(error.exception),
|
|
1445
|
-
)
|
|
1446
|
-
# Check if it catches the cf.key with hyphens.
|
|
1447
|
-
cf1.key = "test-1-custom-field"
|
|
1448
|
-
with self.assertRaises(ValidationError) as error:
|
|
1449
|
-
cf1.validated_save()
|
|
1450
|
-
self.assertIn(
|
|
1451
|
-
"This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
|
|
1452
|
-
str(error.exception),
|
|
1453
|
-
)
|
|
1454
|
-
# Check if it catches the cf.key with special characters
|
|
1455
|
-
cf1.key = "test_1_custom_f)(&d"
|
|
1456
|
-
with self.assertRaises(ValidationError) as error:
|
|
1457
|
-
cf1.validated_save()
|
|
1458
|
-
self.assertIn(
|
|
1459
|
-
"This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
|
|
1460
|
-
str(error.exception),
|
|
1461
|
-
)
|
|
1437
|
+
cf1 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Test 1")
|
|
1438
|
+
for invalid_key in [
|
|
1439
|
+
"12_test_1", # Check if it catches the cf.key starting with a digit.
|
|
1440
|
+
"test 1", # Check if it catches the cf.key with whitespace.
|
|
1441
|
+
"test-1-custom-field", # Check if it catches the cf.key with hyphens.
|
|
1442
|
+
"test_1_custom_f)(&d", # Check if it catches the cf.key with special characters
|
|
1443
|
+
]:
|
|
1444
|
+
with self.assertRaisesRegex(
|
|
1445
|
+
ValidationError,
|
|
1446
|
+
"This key is not Python/GraphQL safe. "
|
|
1447
|
+
"Please do not start the key with a digit and do not use hyphens or whitespace",
|
|
1448
|
+
):
|
|
1449
|
+
cf1.key = invalid_key
|
|
1450
|
+
cf1.validated_save()
|
|
1462
1451
|
|
|
1463
1452
|
|
|
1464
1453
|
class CustomFieldFilterTest(TestCase):
|
|
@@ -1934,9 +1923,11 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
|
|
|
1934
1923
|
)
|
|
1935
1924
|
self.cf.save()
|
|
1936
1925
|
self.cf.content_types.set([obj_type])
|
|
1926
|
+
self.assertEqual(self.cf.choices, [])
|
|
1937
1927
|
|
|
1938
1928
|
self.choice = CustomFieldChoice(custom_field=self.cf, value="Foo")
|
|
1939
1929
|
self.choice.save()
|
|
1930
|
+
self.assertEqual(self.cf.choices, ["Foo"])
|
|
1940
1931
|
|
|
1941
1932
|
location_status = Status.objects.get_for_model(Location).first()
|
|
1942
1933
|
self.location_type = LocationType.objects.get(name="Campus")
|
|
@@ -1952,7 +1943,7 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
|
|
|
1952
1943
|
|
|
1953
1944
|
def test_default_value_must_be_valid_choice_sad_path(self):
|
|
1954
1945
|
self.cf.default = "invalid value"
|
|
1955
|
-
with self.
|
|
1946
|
+
with self.assertRaisesRegex(ValidationError, 'Invalid default value "invalid value"'):
|
|
1956
1947
|
self.cf.full_clean()
|
|
1957
1948
|
|
|
1958
1949
|
def test_default_value_must_be_valid_choice_happy_path(self):
|
|
@@ -1965,6 +1956,13 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
|
|
|
1965
1956
|
with self.assertRaises(ProtectedError):
|
|
1966
1957
|
self.choice.delete()
|
|
1967
1958
|
|
|
1959
|
+
def test_inactive_choice_can_be_deleted(self):
|
|
1960
|
+
self.location._custom_field_data.pop("cf1")
|
|
1961
|
+
self.location.validated_save()
|
|
1962
|
+
self.assertEqual(self.cf.choices, ["Foo"])
|
|
1963
|
+
self.choice.delete()
|
|
1964
|
+
self.assertEqual(self.cf.choices, [])
|
|
1965
|
+
|
|
1968
1966
|
def test_custom_choice_deleted_with_field(self):
|
|
1969
1967
|
self.cf.delete()
|
|
1970
1968
|
self.assertEqual(CustomField.objects.count(), 1) # custom field automatically added by the Example App
|
|
@@ -30,6 +30,7 @@ from nautobot.extras.models import (
|
|
|
30
30
|
ConfigContextSchema,
|
|
31
31
|
ExportTemplate,
|
|
32
32
|
GitRepository,
|
|
33
|
+
GraphQLQuery,
|
|
33
34
|
Job,
|
|
34
35
|
JobButton,
|
|
35
36
|
JobHook,
|
|
@@ -197,6 +198,15 @@ class GitTest(TransactionTestCase):
|
|
|
197
198
|
)
|
|
198
199
|
self.assertIsNotNone(export_template_vlan)
|
|
199
200
|
|
|
201
|
+
def assert_graphql_query_exists(self, name="device_names.gql"):
|
|
202
|
+
"""Helper function to assert Graphql query exists."""
|
|
203
|
+
graphql_query = GraphQLQuery.objects.get(
|
|
204
|
+
name=name,
|
|
205
|
+
owner_object_id=self.repo.pk,
|
|
206
|
+
owner_content_type=ContentType.objects.get_for_model(GitRepository),
|
|
207
|
+
)
|
|
208
|
+
self.assertIsNotNone(graphql_query)
|
|
209
|
+
|
|
200
210
|
def assert_job_exists(self, name="MyJob", installed=True):
|
|
201
211
|
"""Helper function to assert JobModel and registered Job exist."""
|
|
202
212
|
# Is it registered correctly in the database?
|
|
@@ -348,6 +358,10 @@ class GitTest(TransactionTestCase):
|
|
|
348
358
|
# Case when ContentType.model != ContentType.name, template was added and deleted during sync (#570)
|
|
349
359
|
self.assert_export_template_vlan_exists("template.j2")
|
|
350
360
|
|
|
361
|
+
# Make sure Graphgl queries were loaded
|
|
362
|
+
self.assert_graphql_query_exists("device_names")
|
|
363
|
+
self.assert_graphql_query_exists("device_interfaces")
|
|
364
|
+
|
|
351
365
|
# Make sure Jobs were successfully loaded from file and registered as JobModels
|
|
352
366
|
self.assert_job_exists(name="MyJob")
|
|
353
367
|
self.assert_job_exists(name="MyJobButtonReceiver")
|
|
@@ -439,6 +453,7 @@ class GitTest(TransactionTestCase):
|
|
|
439
453
|
self.assertFalse(ConfigContextSchema.objects.filter(owner_object_id=self.repo.id).exists())
|
|
440
454
|
self.assertFalse(ConfigContext.objects.filter(owner_object_id=self.repo.id).exists())
|
|
441
455
|
self.assertFalse(ExportTemplate.objects.filter(owner_object_id=self.repo.id).exists())
|
|
456
|
+
self.assertFalse(GraphQLQuery.objects.filter(owner_object_id=self.repo.id).exists())
|
|
442
457
|
self.assertFalse(Job.objects.filter(module_name__startswith=f"{self.repo.slug}.").exists())
|
|
443
458
|
device = Device.objects.get(name=self.device.name)
|
|
444
459
|
self.assertIsNone(device.local_config_context_data)
|
|
@@ -505,6 +520,11 @@ class GitTest(TransactionTestCase):
|
|
|
505
520
|
grouping="jobs",
|
|
506
521
|
message__contains="Error in loading Jobs from Git repository: ",
|
|
507
522
|
)
|
|
523
|
+
failure_logs.get(
|
|
524
|
+
grouping="graphql queries",
|
|
525
|
+
message__contains="Error processing GraphQL query file 'bad_device_names.gql': Syntax Error GraphQL (4:5) Expected Name, found }",
|
|
526
|
+
)
|
|
527
|
+
|
|
508
528
|
except (AssertionError, JobLogEntry.DoesNotExist):
|
|
509
529
|
for log in log_entries:
|
|
510
530
|
print(log.message)
|
|
@@ -630,6 +650,8 @@ class GitTest(TransactionTestCase):
|
|
|
630
650
|
self.assert_export_template_device("template.j2")
|
|
631
651
|
self.assert_export_template_html_exist("template2.html")
|
|
632
652
|
self.assert_export_template_vlan_exists("template.j2")
|
|
653
|
+
self.assert_graphql_query_exists(name="device_names")
|
|
654
|
+
self.assert_graphql_query_exists(name="device_interfaces")
|
|
633
655
|
self.assert_job_exists(name="MyJob")
|
|
634
656
|
self.assert_job_exists(name="MyJobButtonReceiver")
|
|
635
657
|
self.assert_job_exists(name="MyJobHookReceiver")
|
|
@@ -669,6 +691,8 @@ class GitTest(TransactionTestCase):
|
|
|
669
691
|
self.assert_export_template_device("template.j2")
|
|
670
692
|
self.assert_export_template_html_exist("template2.html")
|
|
671
693
|
self.assert_export_template_vlan_exists("template.j2")
|
|
694
|
+
self.assert_graphql_query_exists("device_names")
|
|
695
|
+
self.assert_graphql_query_exists("device_interfaces")
|
|
672
696
|
self.assert_job_exists(name="MyJob")
|
|
673
697
|
self.assert_job_exists(name="MyJobButtonReceiver")
|
|
674
698
|
self.assert_job_exists(name="MyJobHookReceiver")
|
|
@@ -711,6 +735,8 @@ class GitTest(TransactionTestCase):
|
|
|
711
735
|
log_entries.get(message__contains="Addition - `export_templates/dcim/device/template.j2`")
|
|
712
736
|
log_entries.get(message__contains="Addition - `export_templates/dcim/device/template2.html`")
|
|
713
737
|
log_entries.get(message__contains="Addition - `export_templates/ipam/vlan/template.j2`")
|
|
738
|
+
log_entries.get(message__contains="Addition - `graphql_queries/device_interfaces.gql`")
|
|
739
|
+
log_entries.get(message__contains="Addition - `graphql_queries/device_names.gql`")
|
|
714
740
|
log_entries.get(message__contains="Addition - `jobs/__init__.py`")
|
|
715
741
|
log_entries.get(message__contains="Addition - `jobs/my_job.py`")
|
|
716
742
|
except JobLogEntry.DoesNotExist:
|
|
@@ -721,6 +747,7 @@ class GitTest(TransactionTestCase):
|
|
|
721
747
|
self.assertFalse(ConfigContextSchema.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
722
748
|
self.assertFalse(ConfigContext.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
723
749
|
self.assertFalse(ExportTemplate.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
750
|
+
self.assertFalse(GraphQLQuery.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
724
751
|
self.assertFalse(Job.objects.filter(module_name__startswith=self.repo.slug).exists())
|
|
725
752
|
|
|
726
753
|
# TODO: test dry-run against a branch name
|
|
@@ -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(
|