nautobot 2.2.5__py3-none-any.whl → 2.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/api.py +2 -0
- nautobot/apps/models.py +2 -0
- nautobot/core/api/fields.py +13 -0
- nautobot/core/api/mixins.py +6 -1
- nautobot/core/api/schema.py +3 -1
- nautobot/core/api/serializers.py +7 -1
- nautobot/core/celery/__init__.py +1 -1
- nautobot/core/management/commands/generate_test_data.py +128 -158
- nautobot/core/models/fields.py +15 -0
- nautobot/core/tests/runner.py +10 -0
- nautobot/core/tests/test_utils.py +48 -1
- nautobot/core/utils/git.py +121 -49
- nautobot/core/utils/module_loading.py +10 -2
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/tests/test_models.py +2 -0
- nautobot/extras/datasources/git.py +133 -135
- nautobot/extras/datasources/utils.py +3 -0
- nautobot/extras/filters/__init__.py +1 -0
- nautobot/extras/forms/forms.py +16 -3
- nautobot/extras/jobs.py +9 -1
- nautobot/extras/migrations/0107_laxurlfield.py +28 -0
- nautobot/extras/migrations/0108_jobbutton_enabled.py +17 -0
- nautobot/extras/models/datasources.py +6 -4
- nautobot/extras/models/groups.py +9 -2
- nautobot/extras/models/jobs.py +30 -0
- nautobot/extras/models/models.py +2 -4
- nautobot/extras/tables.py +3 -0
- nautobot/extras/templates/extras/jobbutton_retrieve.html +6 -2
- nautobot/extras/templatetags/job_buttons.py +2 -2
- nautobot/extras/tests/git_data/01-valid-files/__init__.py +0 -0
- nautobot/extras/tests/git_data/01-valid-files/config_context_schemas/schema-1.yaml +18 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/context.yaml +12 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/devices/test-device.json +3 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/locations/Test Location.json +7 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template.j2 +3 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template2.html +4 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/ipam/vlan/template.j2 +3 -0
- nautobot/extras/tests/git_data/01-valid-files/jobs/__init__.py +5 -0
- nautobot/extras/tests/git_data/01-valid-files/jobs/my_job.py +16 -0
- nautobot/extras/tests/git_data/02-invalid-files/__init__.py +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema1.json +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema2.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext1.json +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext2.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext3.json +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/devices/nosuchdevice.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/dcim/template.j2 +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/devices/template.j2 +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/export_templates/dcim/nosuchmodel/template.j2 +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/export_templates/nosuchapp/device/template.j2 +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/__init__.py +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/importerror.py +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/syntaxerror.py +1 -0
- nautobot/extras/tests/git_helper.py +76 -0
- nautobot/extras/tests/test_api.py +28 -11
- nautobot/extras/tests/test_datasources.py +94 -276
- nautobot/extras/tests/test_dynamicgroups.py +8 -1
- nautobot/extras/tests/test_models.py +8 -3
- nautobot/extras/tests/test_views.py +21 -7
- nautobot/extras/views.py +1 -1
- nautobot/ipam/api/serializers.py +46 -16
- nautobot/ipam/api/views.py +29 -16
- nautobot/ipam/filters.py +9 -1
- nautobot/ipam/forms.py +8 -0
- nautobot/ipam/tables.py +1 -1
- nautobot/ipam/tests/test_api.py +15 -20
- nautobot/ipam/tests/test_filters.py +15 -0
- nautobot/project-static/docs/404.html +83 -8
- nautobot/project-static/docs/apps/index.html +96 -10
- nautobot/project-static/docs/apps/nautobot-apps.html +96 -10
- nautobot/project-static/docs/assets/app-icons/icon-CapacityMetrics.svg +1 -0
- nautobot/project-static/docs/assets/app-icons/icon-CircuitMaintenance.png +0 -0
- nautobot/project-static/docs/assets/javascripts/{bundle.ebd0bdb7.min.js → bundle.fe8b6f2b.min.js} +4 -4
- nautobot/project-static/docs/assets/javascripts/{bundle.ebd0bdb7.min.js.map → bundle.fe8b6f2b.min.js.map} +3 -3
- nautobot/project-static/docs/assets/javascripts/glightbox.min.js +1 -0
- nautobot/project-static/docs/assets/stylesheets/glightbox.min.css +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +156 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +106 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +96 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +143 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +96 -10
- nautobot/project-static/docs/development/apps/api/configuration-view.html +96 -10
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +96 -10
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +96 -10
- nautobot/project-static/docs/development/apps/api/models/global-search.html +96 -10
- nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -10
- nautobot/project-static/docs/development/apps/api/models/index.html +96 -10
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +96 -10
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +96 -10
- nautobot/project-static/docs/development/apps/api/prometheus.html +96 -10
- nautobot/project-static/docs/development/apps/api/setup.html +96 -10
- nautobot/project-static/docs/development/apps/api/testing.html +96 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +96 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +96 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +96 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +96 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/base-template.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/index.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/notes.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +96 -10
- nautobot/project-static/docs/development/apps/api/views/urls.html +96 -10
- nautobot/project-static/docs/development/apps/index.html +96 -10
- nautobot/project-static/docs/development/apps/migration/code-updates.html +97 -11
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +96 -10
- nautobot/project-static/docs/development/apps/migration/from-v1.html +96 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +96 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +96 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +96 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +96 -10
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +96 -10
- nautobot/project-static/docs/development/core/application-registry.html +96 -10
- nautobot/project-static/docs/development/core/best-practices.html +96 -10
- nautobot/project-static/docs/development/core/bootstrap-ui.html +96 -10
- nautobot/project-static/docs/development/core/caching.html +96 -10
- nautobot/project-static/docs/development/core/controllers.html +96 -10
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +96 -10
- nautobot/project-static/docs/development/core/generic-views.html +96 -10
- nautobot/project-static/docs/development/core/getting-started.html +98 -13
- nautobot/project-static/docs/development/core/homepage.html +96 -10
- nautobot/project-static/docs/development/core/index.html +96 -10
- nautobot/project-static/docs/development/core/model-checklist.html +96 -10
- nautobot/project-static/docs/development/core/model-features.html +96 -10
- nautobot/project-static/docs/development/core/natural-keys.html +96 -10
- nautobot/project-static/docs/development/core/navigation-menu.html +96 -10
- nautobot/project-static/docs/development/core/release-checklist.html +96 -10
- nautobot/project-static/docs/development/core/role-internals.html +96 -10
- nautobot/project-static/docs/development/core/settings.html +96 -10
- nautobot/project-static/docs/development/core/style-guide.html +96 -10
- nautobot/project-static/docs/development/core/templates.html +96 -10
- nautobot/project-static/docs/development/core/testing.html +109 -11
- nautobot/project-static/docs/development/core/user-preferences.html +96 -10
- nautobot/project-static/docs/development/index.html +96 -10
- nautobot/project-static/docs/development/jobs/index.html +96 -10
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +96 -10
- nautobot/project-static/docs/index.html +13 -8362
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +8229 -0
- nautobot/project-static/docs/overview/design_philosophy.html +8158 -0
- nautobot/project-static/docs/overview/index.html +8230 -0
- nautobot/project-static/docs/release-notes/index.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.0.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.1.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.2.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.3.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.4.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.5.html +96 -10
- nautobot/project-static/docs/release-notes/version-1.6.html +96 -10
- nautobot/project-static/docs/release-notes/version-2.0.html +96 -10
- nautobot/project-static/docs/release-notes/version-2.1.html +96 -10
- nautobot/project-static/docs/release-notes/version-2.2.html +516 -131
- nautobot/project-static/docs/requirements.txt +2 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +268 -258
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +96 -10
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +96 -10
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +96 -10
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +96 -10
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +96 -10
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +96 -10
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +96 -10
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/index.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation/services.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +96 -10
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +96 -10
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +96 -10
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +96 -10
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +96 -10
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +96 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +96 -10
- 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 +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +96 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +96 -10
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +96 -10
- nautobot/project-static/docs/user-guide/index.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +101 -14
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +96 -10
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +96 -10
- nautobot/project-static/js/connection_toggles.js +7 -6
- {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/METADATA +2 -2
- {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/RECORD +344 -311
- nautobot/extras/tests/test_git.py +0 -23
- {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/NOTICE +0 -0
- {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/WHEEL +0 -0
- {nautobot-2.2.5.dist-info → nautobot-2.2.7.dist-info}/entry_points.txt +0 -0
nautobot/apps/api.py
CHANGED
|
@@ -12,6 +12,7 @@ from nautobot.core.api import (
|
|
|
12
12
|
from nautobot.core.api.fields import (
|
|
13
13
|
ChoiceField,
|
|
14
14
|
ContentTypeField,
|
|
15
|
+
LaxURLField,
|
|
15
16
|
NautobotHyperlinkedRelatedField,
|
|
16
17
|
ObjectTypeField,
|
|
17
18
|
SerializedPKRelatedField,
|
|
@@ -63,6 +64,7 @@ __all__ = (
|
|
|
63
64
|
"get_view_name",
|
|
64
65
|
"GetObjectCountsView",
|
|
65
66
|
"is_api_request",
|
|
67
|
+
"LaxURLField",
|
|
66
68
|
"ModelViewSet",
|
|
67
69
|
"ModelViewSetMixin",
|
|
68
70
|
"MultipleChoiceJSONField",
|
nautobot/apps/models.py
CHANGED
|
@@ -8,6 +8,7 @@ from nautobot.core.models.fields import (
|
|
|
8
8
|
ForeignKeyLimitedByContentTypes,
|
|
9
9
|
ForeignKeyWithAutoRelatedName,
|
|
10
10
|
JSONArrayField,
|
|
11
|
+
LaxURLField,
|
|
11
12
|
mac_unix_expanded_uppercase,
|
|
12
13
|
MACAddressCharField,
|
|
13
14
|
NaturalOrderingField,
|
|
@@ -83,6 +84,7 @@ __all__ = (
|
|
|
83
84
|
"is_taggable",
|
|
84
85
|
"JSONArrayField",
|
|
85
86
|
"JSONBAgg",
|
|
87
|
+
"LaxURLField",
|
|
86
88
|
"mac_unix_expanded_uppercase",
|
|
87
89
|
"MACAddressCharField",
|
|
88
90
|
"NameColorContentTypesModel",
|
nautobot/core/api/fields.py
CHANGED
|
@@ -2,15 +2,18 @@ from collections import OrderedDict
|
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
4
|
from django.core.exceptions import ObjectDoesNotExist
|
|
5
|
+
from django.core.validators import URLValidator
|
|
5
6
|
from django.db.models import Model
|
|
6
7
|
from drf_spectacular.utils import extend_schema_field
|
|
7
8
|
from rest_framework import serializers
|
|
8
9
|
from rest_framework.exceptions import ValidationError
|
|
10
|
+
from rest_framework.fields import URLField
|
|
9
11
|
from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
|
|
10
12
|
from timezone_field.rest_framework import TimeZoneSerializerField as TimeZoneSerializerField_
|
|
11
13
|
|
|
12
14
|
from nautobot.core.api.mixins import WritableSerializerMixin
|
|
13
15
|
from nautobot.core.models.utils import deconstruct_composite_key
|
|
16
|
+
from nautobot.core.models.validators import EnhancedURLValidator
|
|
14
17
|
from nautobot.core.utils.data import is_url, is_uuid
|
|
15
18
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
16
19
|
|
|
@@ -124,6 +127,16 @@ class ContentTypeField(RelatedField):
|
|
|
124
127
|
return f"{obj.app_label}.{obj.model}"
|
|
125
128
|
|
|
126
129
|
|
|
130
|
+
class LaxURLField(URLField):
|
|
131
|
+
def __init__(self, validators=None, **kwargs):
|
|
132
|
+
super().__init__(**kwargs)
|
|
133
|
+
# Discard default URLValidator added by URLField
|
|
134
|
+
self.validators = [v for v in self.validators if not isinstance(v, URLValidator)]
|
|
135
|
+
if validators is not None:
|
|
136
|
+
self.validators.extend(validators)
|
|
137
|
+
self.validators.append(EnhancedURLValidator(message=self.error_messages["invalid"]))
|
|
138
|
+
|
|
139
|
+
|
|
127
140
|
@extend_schema_field(
|
|
128
141
|
{
|
|
129
142
|
"type": "object",
|
nautobot/core/api/mixins.py
CHANGED
|
@@ -6,7 +6,7 @@ from django.core.exceptions import (
|
|
|
6
6
|
MultipleObjectsReturned,
|
|
7
7
|
ObjectDoesNotExist,
|
|
8
8
|
)
|
|
9
|
-
from django.db.models import AutoField
|
|
9
|
+
from django.db.models import AutoField, Model
|
|
10
10
|
from rest_framework.exceptions import ValidationError
|
|
11
11
|
|
|
12
12
|
from nautobot.core.api.utils import dict_to_filter_params
|
|
@@ -70,6 +70,11 @@ class WritableSerializerMixin:
|
|
|
70
70
|
# Strip the trailing slash and split on slashes, taking the last value as the PK.
|
|
71
71
|
data = data.rstrip("/").split("/")[-1]
|
|
72
72
|
|
|
73
|
+
# If we're passing the validated_data from one serializer as input to another serializer,
|
|
74
|
+
# data might already be a model instance:
|
|
75
|
+
if isinstance(data, Model):
|
|
76
|
+
return {"pk": data.pk}
|
|
77
|
+
|
|
73
78
|
try:
|
|
74
79
|
# The int case here is taking into account for the User model we inherit from django
|
|
75
80
|
pk = int(data) if isinstance(queryset.model._meta.pk, AutoField) else uuid.UUID(str(data))
|
nautobot/core/api/schema.py
CHANGED
|
@@ -4,7 +4,7 @@ import re
|
|
|
4
4
|
from drf_spectacular.contrib.django_filters import DjangoFilterExtension
|
|
5
5
|
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
|
|
6
6
|
from drf_spectacular.openapi import AutoSchema
|
|
7
|
-
from drf_spectacular.plumbing import build_array_type, build_media_type_object, get_doc, is_serializer
|
|
7
|
+
from drf_spectacular.plumbing import build_array_type, build_media_type_object, get_doc, is_serializer, list_hash
|
|
8
8
|
from drf_spectacular.serializers import PolymorphicProxySerializerExtension
|
|
9
9
|
from rest_framework import serializers
|
|
10
10
|
from rest_framework.relations import ManyRelatedField
|
|
@@ -359,6 +359,8 @@ class ChoiceFieldFix(OpenApiSerializerFieldExtension):
|
|
|
359
359
|
return {
|
|
360
360
|
"type": value_type,
|
|
361
361
|
"enum": list(choices.keys()),
|
|
362
|
+
# Used to deduplicate enums with the same set of choices, since drf-spectacular 0.27.2
|
|
363
|
+
"x-spec-enum-id": list_hash([(k, v) for k, v in choices.items() if k not in ("", None)]),
|
|
362
364
|
}
|
|
363
365
|
else:
|
|
364
366
|
return {
|
nautobot/core/api/serializers.py
CHANGED
|
@@ -26,12 +26,13 @@ from rest_framework.serializers import SerializerMethodField
|
|
|
26
26
|
from rest_framework.utils.model_meta import _get_to_field, RelationInfo
|
|
27
27
|
|
|
28
28
|
from nautobot.core import constants
|
|
29
|
-
from nautobot.core.api.fields import NautobotHyperlinkedRelatedField, ObjectTypeField
|
|
29
|
+
from nautobot.core.api.fields import LaxURLField, NautobotHyperlinkedRelatedField, ObjectTypeField
|
|
30
30
|
from nautobot.core.api.utils import (
|
|
31
31
|
dict_to_filter_params,
|
|
32
32
|
nested_serializer_factory,
|
|
33
33
|
)
|
|
34
34
|
from nautobot.core.exceptions import ViewConfigException
|
|
35
|
+
from nautobot.core.models.fields import LaxURLField as LaxURLModelField
|
|
35
36
|
from nautobot.core.models.managers import TagsManager
|
|
36
37
|
from nautobot.core.models.utils import construct_composite_key, construct_natural_slug
|
|
37
38
|
from nautobot.core.templatetags.helpers import bettertitle
|
|
@@ -126,6 +127,11 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
|
|
|
126
127
|
to enable the dynamic generation of nested serializers.
|
|
127
128
|
"""
|
|
128
129
|
|
|
130
|
+
serializer_field_mapping = {
|
|
131
|
+
**serializers.ModelSerializer.serializer_field_mapping,
|
|
132
|
+
LaxURLModelField: LaxURLField,
|
|
133
|
+
}
|
|
134
|
+
|
|
129
135
|
serializer_related_field = NautobotHyperlinkedRelatedField
|
|
130
136
|
|
|
131
137
|
display = serializers.SerializerMethodField(read_only=True, help_text="Human friendly display value")
|
nautobot/core/celery/__init__.py
CHANGED
|
@@ -109,7 +109,7 @@ def _import_jobs_from_git_repositories():
|
|
|
109
109
|
and not GitRepository.objects.filter(slug=filename).exists()
|
|
110
110
|
):
|
|
111
111
|
logger.warning("Deleting unmanaged (leftover?) Git repository clone at %s", filepath)
|
|
112
|
-
shutil.rmtree(filepath)
|
|
112
|
+
shutil.rmtree(filepath, ignore_errors=True)
|
|
113
113
|
|
|
114
114
|
# Make sure all GitRepository records that include Jobs have up-to-date git clones, and load their jobs
|
|
115
115
|
for repo in GitRepository.objects.filter(provided_contents__contains="extras.job"):
|
|
@@ -30,6 +30,17 @@ class Command(BaseCommand):
|
|
|
30
30
|
dest="interactive",
|
|
31
31
|
help="Do NOT prompt the user for input or confirmation of any kind.",
|
|
32
32
|
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--print-hashes",
|
|
35
|
+
action="store_true",
|
|
36
|
+
help=(
|
|
37
|
+
"After creating each batch of records, print a hash of the list of all IDs of all objects of "
|
|
38
|
+
"the given type. This is useful for identifying any problems with factory randomness / determinism; "
|
|
39
|
+
"in general, successive runs with the same seed should output identical hashes for each stage, "
|
|
40
|
+
"while successive runs with differing seeds should output different hashes. "
|
|
41
|
+
"Setting environment variable GITHUB_ACTIONS to true is equivalent to specifying this argument."
|
|
42
|
+
),
|
|
43
|
+
)
|
|
33
44
|
parser.add_argument(
|
|
34
45
|
"--cache-test-fixtures",
|
|
35
46
|
action="store_true",
|
|
@@ -46,7 +57,7 @@ class Command(BaseCommand):
|
|
|
46
57
|
help='The database to generate the test data in. Defaults to the "default" database.',
|
|
47
58
|
)
|
|
48
59
|
|
|
49
|
-
def _generate_factory_data(self, seed, db_name):
|
|
60
|
+
def _generate_factory_data(self, seed, db_name, print_hashes=False):
|
|
50
61
|
try:
|
|
51
62
|
import factory.random
|
|
52
63
|
|
|
@@ -83,7 +94,6 @@ class Command(BaseCommand):
|
|
|
83
94
|
from nautobot.extras.utils import TaggableClassesQuery
|
|
84
95
|
from nautobot.ipam.choices import PrefixTypeChoices
|
|
85
96
|
from nautobot.ipam.factory import (
|
|
86
|
-
IPAddressFactory,
|
|
87
97
|
NamespaceFactory,
|
|
88
98
|
PrefixFactory,
|
|
89
99
|
RIRFactory,
|
|
@@ -101,178 +111,135 @@ class Command(BaseCommand):
|
|
|
101
111
|
self.stdout.write(f'Seeding the pseudo-random number generator with seed "{seed}"...')
|
|
102
112
|
factory.random.reseed_random(seed)
|
|
103
113
|
|
|
104
|
-
|
|
114
|
+
def _create_batch(some_factory, count, description="", **kwargs):
|
|
115
|
+
model = some_factory._meta.get_model_class()
|
|
116
|
+
if description:
|
|
117
|
+
description = " " + description
|
|
118
|
+
message = f"Creating {count} {model._meta.verbose_name_plural}{description}..."
|
|
119
|
+
self.stdout.write(message)
|
|
120
|
+
records = some_factory.create_batch(count, using=db_name, **kwargs)
|
|
121
|
+
if print_hashes:
|
|
122
|
+
model_ids = [record.id for record in records]
|
|
123
|
+
sha256_hash = hashlib.sha256(json.dumps(model_ids, cls=DjangoJSONEncoder).encode()).hexdigest()
|
|
124
|
+
self.stdout.write(f" SHA256: {sha256_hash}")
|
|
125
|
+
|
|
105
126
|
populate_role_choices(verbosity=0, using=db_name)
|
|
106
|
-
RoleFactory
|
|
107
|
-
self.stdout.write("Creating Statuses...")
|
|
127
|
+
_create_batch(RoleFactory, 20)
|
|
108
128
|
populate_status_choices(verbosity=0, using=db_name)
|
|
109
|
-
StatusFactory
|
|
110
|
-
self.stdout.write("Creating Tags...")
|
|
129
|
+
_create_batch(StatusFactory, 10)
|
|
111
130
|
# Ensure that we have some tags that are applicable to all relevant content-types
|
|
112
|
-
|
|
131
|
+
_create_batch(
|
|
132
|
+
TagFactory, 5, description="on all content-types", content_types=TaggableClassesQuery().as_queryset()
|
|
133
|
+
)
|
|
113
134
|
# ...and some tags that apply to a random subset of content-types
|
|
114
|
-
TagFactory
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
self.stdout.write("Creating Tenants...")
|
|
123
|
-
TenantFactory.create_batch(10, has_tenant_group=False, using=db_name)
|
|
124
|
-
TenantFactory.create_batch(10, has_tenant_group=True, using=db_name)
|
|
125
|
-
self.stdout.write("Creating LocationTypes...")
|
|
126
|
-
LocationTypeFactory.create_batch(7, using=db_name) # only 7 unique LocationTypes are hard-coded presently
|
|
127
|
-
self.stdout.write("Creating Locations...")
|
|
135
|
+
_create_batch(TagFactory, 15, description="on some content-types")
|
|
136
|
+
_create_batch(ContactFactory, 20)
|
|
137
|
+
_create_batch(TeamFactory, 20)
|
|
138
|
+
_create_batch(TenantGroupFactory, 10, description="without parents", has_parent=False)
|
|
139
|
+
_create_batch(TenantGroupFactory, 10, description="with parents", has_parent=True)
|
|
140
|
+
_create_batch(TenantFactory, 10, description="without a parent group", has_tenant_group=False)
|
|
141
|
+
_create_batch(TenantFactory, 10, description="with a parent group", has_tenant_group=True)
|
|
142
|
+
_create_batch(LocationTypeFactory, 7) # only 7 unique LocationTypes are hard-coded presently
|
|
128
143
|
# First 7 locations must be created in specific order so subsequent objects have valid parents to reference
|
|
129
|
-
LocationFactory
|
|
130
|
-
LocationFactory
|
|
131
|
-
LocationFactory
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
NamespaceFactory.create_batch(10, using=db_name)
|
|
141
|
-
self.stdout.write("Creating VRFs...")
|
|
142
|
-
VRFFactory.create_batch(10, has_tenant=True, using=db_name)
|
|
143
|
-
VRFFactory.create_batch(10, has_tenant=False, using=db_name)
|
|
144
|
-
self.stdout.write("Creating VLANGroups...")
|
|
145
|
-
VLANGroupFactory.create_batch(20, using=db_name)
|
|
146
|
-
self.stdout.write("Creating VLANs...")
|
|
147
|
-
VLANFactory.create_batch(20, using=db_name)
|
|
148
|
-
self.stdout.write("Creating Prefixes and IP Addresses...")
|
|
144
|
+
_create_batch(LocationFactory, 7, description="as structure", has_parent=True)
|
|
145
|
+
_create_batch(LocationFactory, 40)
|
|
146
|
+
_create_batch(LocationFactory, 10, description="without a parent Location", has_parent=False)
|
|
147
|
+
_create_batch(ControllerFactory, 1, description="without a Device or DeviceRedundancyGroup")
|
|
148
|
+
_create_batch(ControllerManagedDeviceGroupFactory, 5, description="to contain Devices")
|
|
149
|
+
_create_batch(RIRFactory, 9) # only 9 unique RIR names are hard-coded presently
|
|
150
|
+
_create_batch(RouteTargetFactory, 20)
|
|
151
|
+
_create_batch(NamespaceFactory, 10)
|
|
152
|
+
_create_batch(VRFFactory, 20)
|
|
153
|
+
_create_batch(VLANGroupFactory, 20)
|
|
154
|
+
_create_batch(VLANFactory, 20)
|
|
149
155
|
for i in range(30):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
ManufacturerFactory
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
DeviceFactory
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
156
|
+
_create_batch(
|
|
157
|
+
PrefixFactory,
|
|
158
|
+
1,
|
|
159
|
+
description=f"(10.{i}.0.0/16 and descendants)",
|
|
160
|
+
prefix=f"10.{i}.0.0/16",
|
|
161
|
+
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
162
|
+
)
|
|
163
|
+
_create_batch(
|
|
164
|
+
PrefixFactory,
|
|
165
|
+
1,
|
|
166
|
+
description=f"(2001:db8:0:{i}::/64 and descendants)",
|
|
167
|
+
prefix=f"2001:db8:0:{i}::/64",
|
|
168
|
+
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
169
|
+
)
|
|
170
|
+
_create_batch(NamespaceFactory, 5, description="without any Prefixes or IPAddresses")
|
|
171
|
+
_create_batch(DeviceFamilyFactory, 20)
|
|
172
|
+
_create_batch(ManufacturerFactory, 8) # First 8 hard-coded Manufacturers
|
|
173
|
+
_create_batch(PlatformFactory, 20, description="with Manufacturers", has_manufacturer=True)
|
|
174
|
+
_create_batch(PlatformFactory, 5, description="without Manufacturers", has_manufacturer=False)
|
|
175
|
+
_create_batch(SoftwareVersionFactory, 20, description="to be usable by Devices")
|
|
176
|
+
_create_batch(SoftwareImageFileFactory, 25, description="to be usable by DeviceTypes")
|
|
177
|
+
_create_batch(ManufacturerFactory, 4, description="without Platforms") # 4 more hard-coded Manufacturers
|
|
178
|
+
_create_batch(DeviceTypeFactory, 30)
|
|
179
|
+
_create_batch(ManufacturerFactory, 2, description="without Platforms or DeviceTypes") # Last 2 hard-coded
|
|
180
|
+
_create_batch(DeviceRedundancyGroupFactory, 20)
|
|
181
|
+
_create_batch(DeviceFactory, 20)
|
|
182
|
+
_create_batch(SoftwareVersionFactory, 5, description="without Devices")
|
|
183
|
+
_create_batch(SoftwareImageFileFactory, 5, description="without DeviceTypes")
|
|
184
|
+
_create_batch(CircuitTypeFactory, 40)
|
|
185
|
+
_create_batch(ProviderFactory, 20, description="to be usable by Circuits")
|
|
186
|
+
_create_batch(ProviderNetworkFactory, 20)
|
|
187
|
+
_create_batch(CircuitFactory, 40)
|
|
188
|
+
_create_batch(ProviderFactory, 20, description="without Circuits")
|
|
189
|
+
# TODO do we really need all of these specifics for CircuitTerminations?
|
|
190
|
+
_create_batch(
|
|
191
|
+
CircuitTerminationFactory, 2, description="with a location, for side A", has_location=True, term_side="A"
|
|
192
|
+
)
|
|
193
|
+
_create_batch(
|
|
194
|
+
CircuitTerminationFactory, 2, description="with a location, for side Z", has_location=True, term_side="Z"
|
|
195
|
+
)
|
|
196
|
+
_create_batch(
|
|
197
|
+
CircuitTerminationFactory,
|
|
198
|
+
2,
|
|
199
|
+
description="without a location, for side A",
|
|
200
|
+
has_location=False,
|
|
201
|
+
term_side="A",
|
|
202
|
+
)
|
|
203
|
+
_create_batch(
|
|
204
|
+
CircuitTerminationFactory,
|
|
205
|
+
2,
|
|
206
|
+
description="without a location, for side Z",
|
|
207
|
+
has_location=False,
|
|
208
|
+
term_side="Z",
|
|
209
|
+
)
|
|
210
|
+
_create_batch(
|
|
211
|
+
CircuitTerminationFactory,
|
|
212
|
+
2,
|
|
213
|
+
description="with port_speed but without upstream_speed",
|
|
214
|
+
has_port_speed=True,
|
|
215
|
+
has_upstream_speed=False,
|
|
216
|
+
)
|
|
217
|
+
_create_batch(
|
|
218
|
+
CircuitTerminationFactory,
|
|
219
|
+
2,
|
|
220
|
+
description="with a location, port_speed, upstream_speed, xconnect_id, pp_info, and description",
|
|
198
221
|
has_location=True,
|
|
199
222
|
has_port_speed=True,
|
|
200
223
|
has_upstream_speed=True,
|
|
201
224
|
has_xconnect_id=True,
|
|
202
225
|
has_pp_info=True,
|
|
203
226
|
has_description=True,
|
|
204
|
-
using=db_name,
|
|
205
227
|
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
ControllerFactory.create_batch(10)
|
|
210
|
-
ControllerManagedDeviceGroupFactory.create_batch(30)
|
|
228
|
+
_create_batch(ExternalIntegrationFactory, 20)
|
|
229
|
+
_create_batch(ControllerFactory, 10, description="with Devices or DeviceRedundancyGroups")
|
|
230
|
+
_create_batch(ControllerManagedDeviceGroupFactory, 5, description="without any Devices")
|
|
211
231
|
# make sure we have some tenants that have null relationships to make filter tests happy
|
|
212
|
-
|
|
213
|
-
TenantFactory.create_batch(10, using=db_name)
|
|
232
|
+
_create_batch(TenantFactory, 10, description="without any associated objects")
|
|
214
233
|
# TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
|
|
215
|
-
# UserFactory
|
|
216
|
-
# RackFactory
|
|
217
|
-
# RackReservationFactory
|
|
218
|
-
# ClusterTypeFactory
|
|
219
|
-
# ClusterGroupFactory
|
|
220
|
-
# ClusterFactory
|
|
221
|
-
# VirtualMachineFactory
|
|
234
|
+
# _create_batch(UserFactory, 10)
|
|
235
|
+
# _create_batch(RackFactory, 10)
|
|
236
|
+
# _create_batch(RackReservationFactory, 10)
|
|
237
|
+
# _create_batch(ClusterTypeFactory, 10)
|
|
238
|
+
# _create_batch(ClusterGroupFactory, 10)
|
|
239
|
+
# _create_batch(ClusterFactory, 10)
|
|
240
|
+
# _create_batch(VirtualMachineFactory, 10)
|
|
222
241
|
# We need to remove them from there and enable them here instead, but that will require many test updates.
|
|
223
242
|
|
|
224
|
-
self._output_hash_for_factory_models(
|
|
225
|
-
factories=[
|
|
226
|
-
CircuitFactory,
|
|
227
|
-
CircuitTerminationFactory,
|
|
228
|
-
CircuitTypeFactory,
|
|
229
|
-
ContactFactory,
|
|
230
|
-
ControllerManagedDeviceGroupFactory,
|
|
231
|
-
ControllerFactory,
|
|
232
|
-
DeviceFactory,
|
|
233
|
-
DeviceFamilyFactory,
|
|
234
|
-
DeviceRedundancyGroupFactory,
|
|
235
|
-
DeviceTypeFactory,
|
|
236
|
-
ExternalIntegrationFactory,
|
|
237
|
-
IPAddressFactory,
|
|
238
|
-
LocationFactory,
|
|
239
|
-
LocationTypeFactory,
|
|
240
|
-
ManufacturerFactory,
|
|
241
|
-
NamespaceFactory,
|
|
242
|
-
PlatformFactory,
|
|
243
|
-
PrefixFactory,
|
|
244
|
-
ProviderFactory,
|
|
245
|
-
ProviderNetworkFactory,
|
|
246
|
-
RIRFactory,
|
|
247
|
-
RoleFactory,
|
|
248
|
-
RouteTargetFactory,
|
|
249
|
-
SoftwareImageFileFactory,
|
|
250
|
-
SoftwareVersionFactory,
|
|
251
|
-
StatusFactory,
|
|
252
|
-
TagFactory,
|
|
253
|
-
TeamFactory,
|
|
254
|
-
TenantFactory,
|
|
255
|
-
TenantGroupFactory,
|
|
256
|
-
VLANFactory,
|
|
257
|
-
VLANGroupFactory,
|
|
258
|
-
VRFFactory,
|
|
259
|
-
]
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
def _output_hash_for_factory_models(self, factories):
|
|
263
|
-
"""Output a hash of the IDs of all objects in the given factories' model.
|
|
264
|
-
|
|
265
|
-
Used for identifying factory determinism problems in unit tests. Only prints if GITHUB_ACTIONS environment variable is set to "true".
|
|
266
|
-
"""
|
|
267
|
-
if not is_truthy(os.environ.get("GITHUB_ACTIONS", "false")):
|
|
268
|
-
return
|
|
269
|
-
|
|
270
|
-
for factory in factories:
|
|
271
|
-
model = factory._meta.get_model_class()
|
|
272
|
-
model_ids = list(model.objects.order_by("id").values_list("id", flat=True))
|
|
273
|
-
sha256_hash = hashlib.sha256(json.dumps(model_ids, cls=DjangoJSONEncoder).encode()).hexdigest()
|
|
274
|
-
self.stdout.write(f"SHA256 hash for {model.__name__}: {sha256_hash}")
|
|
275
|
-
|
|
276
243
|
def handle(self, *args, **options):
|
|
277
244
|
if options["flush"]:
|
|
278
245
|
if options["interactive"]:
|
|
@@ -298,7 +265,10 @@ Type 'yes' to continue, or 'no' to cancel: """
|
|
|
298
265
|
self.stdout.write(self.style.WARNING(f"Loading factory data from file {options['fixture_file']}"))
|
|
299
266
|
call_command("loaddata", "--database", options["database"], options["fixture_file"])
|
|
300
267
|
else:
|
|
301
|
-
|
|
268
|
+
print_hashes = options["print_hashes"]
|
|
269
|
+
if is_truthy(os.environ.get("GITHUB_ACTIONS", "false")):
|
|
270
|
+
print_hashes = True
|
|
271
|
+
self._generate_factory_data(options["seed"], options["database"], print_hashes=print_hashes)
|
|
302
272
|
|
|
303
273
|
if options["cache_test_fixtures"]:
|
|
304
274
|
self.stdout.write(self.style.WARNING(f"Saving factory data to file {options['fixture_file']}"))
|
nautobot/core/models/fields.py
CHANGED
|
@@ -13,6 +13,7 @@ from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
|
13
13
|
from nautobot.core.forms import fields, widgets
|
|
14
14
|
from nautobot.core.models import ordering
|
|
15
15
|
from nautobot.core.models.managers import TagsManager
|
|
16
|
+
from nautobot.core.models.validators import EnhancedURLValidator
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class mac_unix_expanded_uppercase(mac_unix_expanded):
|
|
@@ -381,6 +382,20 @@ class JSONArrayField(models.JSONField):
|
|
|
381
382
|
)
|
|
382
383
|
|
|
383
384
|
|
|
385
|
+
class LaxURLField(models.URLField):
|
|
386
|
+
"""Like models.URLField, but using validators.EnhancedURLValidator and forms.LaxURLField."""
|
|
387
|
+
|
|
388
|
+
default_validators = [EnhancedURLValidator()]
|
|
389
|
+
|
|
390
|
+
def formfield(self, **kwargs):
|
|
391
|
+
return super().formfield(
|
|
392
|
+
**{
|
|
393
|
+
"form_class": fields.LaxURLField,
|
|
394
|
+
**kwargs,
|
|
395
|
+
},
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
|
|
384
399
|
class TagsField(TaggableManager):
|
|
385
400
|
"""Override FormField method on taggit.managers.TaggableManager to match the Nautobot UI."""
|
|
386
401
|
|
nautobot/core/tests/runner.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
import hashlib
|
|
2
3
|
|
|
3
4
|
from django.conf import settings
|
|
4
5
|
from django.core.management import call_command
|
|
5
6
|
from django.db import connections
|
|
7
|
+
from django.db.migrations.recorder import MigrationRecorder
|
|
6
8
|
from django.test.runner import _init_worker, DiscoverRunner, ParallelTestSuite
|
|
7
9
|
from django.test.utils import get_unique_databases_and_mirrors, NullTimeKeeper, override_settings
|
|
8
10
|
import yaml
|
|
@@ -113,6 +115,14 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
113
115
|
command += ["--seed", settings.TEST_FACTORY_SEED]
|
|
114
116
|
if self.cache_test_fixtures:
|
|
115
117
|
command += ["--cache-test-fixtures"]
|
|
118
|
+
# Use the list of applied migrations as a unique hash to keep fixtures from differing
|
|
119
|
+
# branches/releases of Nautobot in separate files.
|
|
120
|
+
hexdigest = hashlib.shake_128(
|
|
121
|
+
",".join(
|
|
122
|
+
sorted(f"{m.app}.{m.name}" for m in MigrationRecorder.Migration.objects.all())
|
|
123
|
+
).encode("utf-8")
|
|
124
|
+
).hexdigest(10)
|
|
125
|
+
command += ["--fixture-file", f"development/factory_dump.{hexdigest}.json"]
|
|
116
126
|
with time_keeper.timed(f' Pre-populating test database "{alias}" with factory data...'):
|
|
117
127
|
db_command = [*command, "--database", alias]
|
|
118
128
|
call_command(*db_command)
|
|
@@ -4,6 +4,7 @@ from django import forms as django_forms
|
|
|
4
4
|
from django.apps import apps
|
|
5
5
|
from django.contrib.auth.models import Group
|
|
6
6
|
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.core.exceptions import ValidationError
|
|
7
8
|
from django.db.models import Q
|
|
8
9
|
from django.http import QueryDict
|
|
9
10
|
from django.test import TestCase
|
|
@@ -11,7 +12,7 @@ from django.test import TestCase
|
|
|
11
12
|
from nautobot.circuits import models as circuits_models
|
|
12
13
|
from nautobot.core import exceptions, forms, settings_funcs
|
|
13
14
|
from nautobot.core.api import utils as api_utils
|
|
14
|
-
from nautobot.core.models import fields as core_fields, utils as models_utils
|
|
15
|
+
from nautobot.core.models import fields as core_fields, utils as models_utils, validators
|
|
15
16
|
from nautobot.core.utils import data as data_utils, filtering, lookup, requests
|
|
16
17
|
from nautobot.core.utils.migrations import update_object_change_ct_for_replaced_models
|
|
17
18
|
from nautobot.dcim import filters as dcim_filters, forms as dcim_forms, models as dcim_models, tables
|
|
@@ -372,6 +373,52 @@ class SlugifyFunctionsTest(TestCase):
|
|
|
372
373
|
self.assertEqual(core_fields.slugify_dashes_to_underscores(content), expected)
|
|
373
374
|
|
|
374
375
|
|
|
376
|
+
class LaxURLFieldTest(TestCase):
|
|
377
|
+
"""Test LaxURLField and related functionality."""
|
|
378
|
+
|
|
379
|
+
VALID_URLS = [
|
|
380
|
+
"http://example.com",
|
|
381
|
+
"https://local-dns/foo/bar.git", # not supported out-of-the-box by Django, hence our custom classes
|
|
382
|
+
"https://1.1.1.1:8080/",
|
|
383
|
+
"https://[2001:db8::]/",
|
|
384
|
+
]
|
|
385
|
+
INVALID_URLS = [
|
|
386
|
+
"unknown://example.com/",
|
|
387
|
+
"foo:/",
|
|
388
|
+
"http://file://",
|
|
389
|
+
]
|
|
390
|
+
|
|
391
|
+
def test_enhanced_url_validator(self):
|
|
392
|
+
for valid in self.VALID_URLS:
|
|
393
|
+
with self.subTest(valid=valid):
|
|
394
|
+
validators.EnhancedURLValidator()(valid)
|
|
395
|
+
|
|
396
|
+
for invalid in self.INVALID_URLS:
|
|
397
|
+
with self.subTest(invalid=invalid):
|
|
398
|
+
with self.assertRaises(django_forms.ValidationError):
|
|
399
|
+
validators.EnhancedURLValidator()(invalid)
|
|
400
|
+
|
|
401
|
+
def test_forms_lax_url_field(self):
|
|
402
|
+
for valid in self.VALID_URLS:
|
|
403
|
+
with self.subTest(valid=valid):
|
|
404
|
+
forms.LaxURLField().clean(valid)
|
|
405
|
+
|
|
406
|
+
for invalid in self.INVALID_URLS:
|
|
407
|
+
with self.subTest(invalid=invalid):
|
|
408
|
+
with self.assertRaises(django_forms.ValidationError):
|
|
409
|
+
forms.LaxURLField().clean(invalid)
|
|
410
|
+
|
|
411
|
+
def test_models_lax_url_field(self):
|
|
412
|
+
for valid in self.VALID_URLS:
|
|
413
|
+
with self.subTest(valid=valid):
|
|
414
|
+
core_fields.LaxURLField().run_validators(valid)
|
|
415
|
+
|
|
416
|
+
for invalid in self.INVALID_URLS:
|
|
417
|
+
with self.subTest(invalid=invalid):
|
|
418
|
+
with self.assertRaises(ValidationError):
|
|
419
|
+
core_fields.LaxURLField().run_validators(invalid)
|
|
420
|
+
|
|
421
|
+
|
|
375
422
|
class LookupRelatedFunctionTest(TestCase):
|
|
376
423
|
def test_is_single_choice_field(self):
|
|
377
424
|
"""
|