nautobot 2.2.4__py3-none-any.whl → 2.2.6__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/circuits/forms.py +15 -0
- nautobot/circuits/navigation.py +9 -1
- nautobot/circuits/views.py +2 -0
- nautobot/core/api/fields.py +13 -0
- nautobot/core/api/serializers.py +7 -1
- nautobot/core/filters.py +11 -0
- nautobot/core/management/commands/generate_test_data.py +128 -158
- nautobot/core/models/fields.py +15 -0
- nautobot/core/settings.yaml +3 -3
- nautobot/core/testing/filters.py +24 -1
- nautobot/core/testing/views.py +13 -1
- nautobot/core/tests/test_utils.py +48 -1
- nautobot/core/utils/git.py +121 -49
- nautobot/core/utils/module_loading.py +10 -2
- nautobot/core/views/utils.py +18 -1
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filters/__init__.py +1 -1
- nautobot/dcim/forms.py +23 -4
- nautobot/dcim/tables/devicetypes.py +15 -4
- nautobot/dcim/tests/test_models.py +2 -0
- nautobot/dcim/tests/test_views.py +84 -0
- nautobot/dcim/views.py +3 -0
- nautobot/extras/api/views.py +2 -2
- nautobot/extras/context_managers.py +3 -0
- nautobot/extras/datasources/git.py +133 -135
- nautobot/extras/datasources/utils.py +3 -0
- nautobot/extras/filters/__init__.py +16 -1
- nautobot/extras/forms/forms.py +49 -3
- nautobot/extras/forms/mixins.py +0 -6
- 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/jobs.py +30 -0
- nautobot/extras/models/models.py +2 -4
- nautobot/extras/signals.py +6 -1
- 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 +52 -13
- nautobot/extras/tests/test_context_managers.py +33 -1
- nautobot/extras/tests/test_datasources.py +94 -276
- nautobot/extras/tests/test_filters.py +69 -0
- nautobot/extras/tests/test_forms.py +0 -3
- nautobot/extras/tests/test_models.py +8 -3
- nautobot/extras/tests/test_views.py +69 -11
- nautobot/extras/views.py +12 -10
- nautobot/ipam/filters.py +9 -1
- nautobot/ipam/forms.py +26 -0
- nautobot/ipam/tables.py +1 -1
- nautobot/ipam/tests/test_filters.py +15 -0
- nautobot/ipam/tests/test_views.py +9 -2
- nautobot/ipam/views.py +11 -0
- nautobot/project-static/docs/404.html +84 -9
- nautobot/project-static/docs/apps/index.html +97 -11
- nautobot/project-static/docs/apps/nautobot-apps.html +97 -11
- 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.3220b9d7.min.js → bundle.ad660dcc.min.js} +6 -6
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js.map → bundle.ad660dcc.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/assets/stylesheets/{main.66ac8b77.min.css → main.6543a935.min.css} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css.map → main.6543a935.min.css.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +157 -13
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +144 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +97 -11
- nautobot/project-static/docs/development/apps/api/configuration-view.html +97 -11
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/graphql.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +98 -12
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +97 -11
- nautobot/project-static/docs/development/apps/api/prometheus.html +97 -11
- nautobot/project-static/docs/development/apps/api/setup.html +97 -11
- nautobot/project-static/docs/development/apps/api/testing.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/base-template.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/notes.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/urls.html +97 -11
- nautobot/project-static/docs/development/apps/index.html +97 -11
- nautobot/project-static/docs/development/apps/migration/code-updates.html +98 -12
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +97 -11
- nautobot/project-static/docs/development/apps/migration/from-v1.html +99 -13
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +97 -11
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +97 -11
- nautobot/project-static/docs/development/core/application-registry.html +98 -12
- nautobot/project-static/docs/development/core/best-practices.html +97 -11
- nautobot/project-static/docs/development/core/bootstrap-ui.html +97 -11
- nautobot/project-static/docs/development/core/caching.html +97 -11
- nautobot/project-static/docs/development/core/controllers.html +97 -11
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +97 -11
- nautobot/project-static/docs/development/core/generic-views.html +97 -11
- nautobot/project-static/docs/development/core/getting-started.html +99 -13
- nautobot/project-static/docs/development/core/homepage.html +97 -11
- nautobot/project-static/docs/development/core/index.html +97 -11
- nautobot/project-static/docs/development/core/model-checklist.html +97 -11
- nautobot/project-static/docs/development/core/model-features.html +97 -11
- nautobot/project-static/docs/development/core/natural-keys.html +97 -11
- nautobot/project-static/docs/development/core/navigation-menu.html +97 -11
- nautobot/project-static/docs/development/core/release-checklist.html +97 -11
- nautobot/project-static/docs/development/core/role-internals.html +97 -11
- nautobot/project-static/docs/development/core/settings.html +97 -11
- nautobot/project-static/docs/development/core/style-guide.html +97 -11
- nautobot/project-static/docs/development/core/templates.html +97 -11
- nautobot/project-static/docs/development/core/testing.html +98 -12
- nautobot/project-static/docs/development/core/user-preferences.html +97 -11
- nautobot/project-static/docs/development/index.html +97 -11
- nautobot/project-static/docs/development/jobs/index.html +97 -11
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +97 -11
- 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 +97 -11
- nautobot/project-static/docs/release-notes/version-1.0.html +98 -12
- nautobot/project-static/docs/release-notes/version-1.1.html +97 -11
- nautobot/project-static/docs/release-notes/version-1.2.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.3.html +98 -12
- nautobot/project-static/docs/release-notes/version-1.4.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.5.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.6.html +626 -160
- nautobot/project-static/docs/release-notes/version-2.0.html +100 -14
- nautobot/project-static/docs/release-notes/version-2.1.html +97 -11
- nautobot/project-static/docs/release-notes/version-2.2.html +546 -107
- nautobot/project-static/docs/requirements.txt +3 -2
- 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 +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +99 -13
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +98 -12
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +100 -14
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +98 -12
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/index.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +101 -15
- nautobot/project-static/docs/user-guide/administration/installation/services.html +98 -12
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +97 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +97 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +97 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +97 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +98 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +97 -30
- 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 +100 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +99 -13
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +98 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +100 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +98 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +98 -12
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +97 -11
- nautobot/project-static/docs/user-guide/index.html +100 -14
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +98 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +98 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +102 -15
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +98 -44
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +97 -11
- nautobot/project-static/js/forms.js +2 -1
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/views.py +1 -0
- nautobot/virtualization/forms.py +18 -6
- nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
- nautobot/virtualization/views.py +2 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/METADATA +1 -1
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/RECORD +364 -331
- nautobot/extras/tests/test_git.py +0 -23
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/NOTICE +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/WHEEL +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.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/circuits/forms.py
CHANGED
|
@@ -145,6 +145,12 @@ class CircuitTypeForm(NautobotModelForm):
|
|
|
145
145
|
]
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
class CircuitTypeFilterForm(NautobotFilterForm):
|
|
149
|
+
model = CircuitType
|
|
150
|
+
q = forms.CharField(required=False, label="Search")
|
|
151
|
+
name = forms.CharField(required=False)
|
|
152
|
+
|
|
153
|
+
|
|
148
154
|
#
|
|
149
155
|
# Circuits
|
|
150
156
|
#
|
|
@@ -262,3 +268,12 @@ class CircuitTerminationForm(LocatableModelFormMixin, NautobotModelForm):
|
|
|
262
268
|
widgets = {
|
|
263
269
|
"term_side": forms.HiddenInput(),
|
|
264
270
|
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class CircuitTerminationFilterForm(LocatableModelFilterFormMixin, NautobotFilterForm):
|
|
274
|
+
model = CircuitTermination
|
|
275
|
+
q = forms.CharField(required=False, label="Search")
|
|
276
|
+
circuit = DynamicModelMultipleChoiceField(queryset=Circuit.objects.all(), to_field_name="cid", required=False)
|
|
277
|
+
provider_network = DynamicModelMultipleChoiceField(
|
|
278
|
+
queryset=ProviderNetwork.objects.all(), to_field_name="name", required=False
|
|
279
|
+
)
|
nautobot/circuits/navigation.py
CHANGED
|
@@ -33,10 +33,18 @@ menu_items = (
|
|
|
33
33
|
),
|
|
34
34
|
),
|
|
35
35
|
),
|
|
36
|
+
NavMenuItem(
|
|
37
|
+
link="circuits:circuittermination_list",
|
|
38
|
+
name="Circuit Terminations",
|
|
39
|
+
weight=200,
|
|
40
|
+
permissions=[
|
|
41
|
+
"circuits.view_circuittermination",
|
|
42
|
+
],
|
|
43
|
+
),
|
|
36
44
|
NavMenuItem(
|
|
37
45
|
link="circuits:circuittype_list",
|
|
38
46
|
name="Circuit Types",
|
|
39
|
-
weight=
|
|
47
|
+
weight=300,
|
|
40
48
|
permissions=[
|
|
41
49
|
"circuits.view_circuittype",
|
|
42
50
|
],
|
nautobot/circuits/views.py
CHANGED
|
@@ -27,6 +27,7 @@ class CircuitTypeUIViewSet(
|
|
|
27
27
|
view_mixins.ObjectNotesViewMixin,
|
|
28
28
|
):
|
|
29
29
|
filterset_class = filters.CircuitTypeFilterSet
|
|
30
|
+
filterset_form_class = forms.CircuitTypeFilterForm
|
|
30
31
|
form_class = forms.CircuitTypeForm
|
|
31
32
|
queryset = CircuitType.objects.annotate(circuit_count=count_related(Circuit, "circuit_type"))
|
|
32
33
|
serializer_class = serializers.CircuitTypeSerializer
|
|
@@ -67,6 +68,7 @@ class CircuitTerminationUIViewSet(
|
|
|
67
68
|
):
|
|
68
69
|
action_buttons = ("import", "export")
|
|
69
70
|
filterset_class = filters.CircuitTerminationFilterSet
|
|
71
|
+
filterset_form_class = forms.CircuitTerminationFilterForm
|
|
70
72
|
form_class = forms.CircuitTerminationForm
|
|
71
73
|
queryset = CircuitTermination.objects.all()
|
|
72
74
|
serializer_class = serializers.CircuitTerminationSerializer
|
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/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/filters.py
CHANGED
|
@@ -176,6 +176,9 @@ class ContentTypeFilterMixin:
|
|
|
176
176
|
if value in EMPTY_VALUES:
|
|
177
177
|
return qs
|
|
178
178
|
|
|
179
|
+
if value.isdigit():
|
|
180
|
+
return qs.filter(**{f"{self.field_name}__pk": value})
|
|
181
|
+
|
|
179
182
|
try:
|
|
180
183
|
app_label, model = value.lower().split(".")
|
|
181
184
|
except ValueError:
|
|
@@ -242,6 +245,9 @@ class ContentTypeMultipleChoiceFilter(django_filters.MultipleChoiceFilter):
|
|
|
242
245
|
if self.conjoined:
|
|
243
246
|
qs = ContentTypeFilter.filter(self, qs, v)
|
|
244
247
|
else:
|
|
248
|
+
if v.isdigit():
|
|
249
|
+
q |= models.Q(**{f"{self.field_name}__pk": value})
|
|
250
|
+
continue
|
|
245
251
|
# Similar to the ContentTypeFilter.filter() call above, but instead of narrowing the query each time
|
|
246
252
|
# (a AND b AND c ...) we broaden the query each time (a OR b OR c ...).
|
|
247
253
|
# Specifically, we're mapping a value like ['dcim.device', 'ipam.vlan'] to a query like
|
|
@@ -716,6 +722,11 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
716
722
|
"""
|
|
717
723
|
filters = super().get_filters()
|
|
718
724
|
|
|
725
|
+
# Remove any filters that may have been auto-generated from private model attributes
|
|
726
|
+
for filter_name in list(filters.keys()):
|
|
727
|
+
if filter_name.startswith("_"):
|
|
728
|
+
del filters[filter_name]
|
|
729
|
+
|
|
719
730
|
# django-filters has no concept of "abstract" filtersets, so we have to fake it
|
|
720
731
|
if cls._meta.model is not None:
|
|
721
732
|
new_filters = {}
|
|
@@ -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/settings.yaml
CHANGED
|
@@ -773,7 +773,7 @@ properties:
|
|
|
773
773
|
description: "A mapping of permissions to assign a new user account when created using SSO authentication."
|
|
774
774
|
details: |-
|
|
775
775
|
Each key in the dictionary will be the permission name specified as `<app_label>.<action>_<model>`,
|
|
776
|
-
and the value should be set to the permission [constraints](../guides/permissions.md#
|
|
776
|
+
and the value should be set to the permission [constraints](../guides/permissions.md#example-constraint-definitions),
|
|
777
777
|
or `None` to allow all objects.
|
|
778
778
|
|
|
779
779
|
Example:
|
|
@@ -1278,7 +1278,7 @@ properties:
|
|
|
1278
1278
|
|
|
1279
1279
|
!!! note
|
|
1280
1280
|
If a given device has an appropriately populated
|
|
1281
|
-
[secrets group](../../platform-functionality/secret.md#
|
|
1281
|
+
[secrets group](../../platform-functionality/secret.md#secrets-groups) assigned to it,
|
|
1282
1282
|
the [secrets](../../platform-functionality/secret.md) defined in that group will take precedence
|
|
1283
1283
|
over these default values.
|
|
1284
1284
|
environment_variable: "NAUTOBOT_NAPALM_PASSWORD"
|
|
@@ -1300,7 +1300,7 @@ properties:
|
|
|
1300
1300
|
|
|
1301
1301
|
!!! note
|
|
1302
1302
|
If a given device has an appropriately populated
|
|
1303
|
-
[secrets group](../../platform-functionality/secret.md#
|
|
1303
|
+
[secrets group](../../platform-functionality/secret.md#secrets-groups) assigned to it,
|
|
1304
1304
|
the [secrets](../../platform-functionality/secret.md) defined in that group will take precedence
|
|
1305
1305
|
over these default values.
|
|
1306
1306
|
environment_variable: "NAUTOBOT_NAPALM_USERNAME"
|
nautobot/core/testing/filters.py
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import random
|
|
2
2
|
import string
|
|
3
3
|
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
4
5
|
from django.db.models import Count, Q
|
|
5
6
|
from django.db.models.fields.related import ManyToManyField
|
|
6
7
|
from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
|
|
7
8
|
from django.test import tag
|
|
8
9
|
|
|
9
|
-
from nautobot.core.filters import
|
|
10
|
+
from nautobot.core.filters import (
|
|
11
|
+
ContentTypeChoiceFilter,
|
|
12
|
+
ContentTypeFilter,
|
|
13
|
+
ContentTypeMultipleChoiceFilter,
|
|
14
|
+
RelatedMembershipBooleanFilter,
|
|
15
|
+
SearchFilter,
|
|
16
|
+
)
|
|
10
17
|
from nautobot.core.models.generics import PrimaryModel
|
|
11
18
|
from nautobot.core.testing import views
|
|
12
19
|
from nautobot.tenancy import models
|
|
@@ -286,6 +293,22 @@ class FilterTestCases:
|
|
|
286
293
|
obj, obj_field_name = self._get_nested_related_obj_and_its_field_name(obj, obj_field_name)
|
|
287
294
|
self._assert_q_filter_predicate_validity(obj, obj_field_name, filter_field_name, lookup_method)
|
|
288
295
|
|
|
296
|
+
def test_content_type_related_fields_uses_content_type_filter(self):
|
|
297
|
+
for field in self.queryset.model._meta.fields:
|
|
298
|
+
related_model = getattr(field, "related_model", None)
|
|
299
|
+
if not related_model or related_model != ContentType:
|
|
300
|
+
continue
|
|
301
|
+
with self.subTest(
|
|
302
|
+
f"Assert {self.filterset.__class__.__name__}.{field.name} implements ContentTypeFilter"
|
|
303
|
+
):
|
|
304
|
+
filter_field = self.filterset.get_filters().get(field.name)
|
|
305
|
+
if not filter_field:
|
|
306
|
+
# This field is not part of the Filterset.
|
|
307
|
+
continue
|
|
308
|
+
self.assertIsInstance(
|
|
309
|
+
filter_field, (ContentTypeFilter, ContentTypeMultipleChoiceFilter, ContentTypeChoiceFilter)
|
|
310
|
+
)
|
|
311
|
+
|
|
289
312
|
class NameOnlyFilterTestCase(FilterTestCase):
|
|
290
313
|
"""Add simple tests for filtering by name."""
|
|
291
314
|
|