nautobot 2.4.1__py3-none-any.whl → 2.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
- nautobot/circuits/tests/integration/test_relationships.py +1 -1
- nautobot/core/apps/__init__.py +0 -5
- nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
- nautobot/core/testing/integration.py +437 -10
- nautobot/core/tests/test_jobs.py +34 -2
- nautobot/core/utils/git.py +7 -2
- nautobot/core/views/generic.py +1 -1
- nautobot/core/views/mixins.py +13 -6
- nautobot/core/views/utils.py +2 -2
- nautobot/dcim/forms.py +12 -0
- nautobot/dcim/tables/devices.py +2 -1
- nautobot/dcim/templates/dcim/cable.html +1 -1
- nautobot/dcim/templates/dcim/device/base.html +1 -1
- nautobot/dcim/templates/dcim/device.html +2 -2
- nautobot/dcim/templates/dcim/device_component.html +1 -1
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/locationtype.html +1 -1
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/manufacturer.html +1 -1
- nautobot/dcim/templates/dcim/platform.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rackgroup.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation.html +2 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
- nautobot/dcim/tests/test_views.py +9 -1
- nautobot/dcim/views.py +12 -15
- nautobot/extras/api/serializers.py +33 -0
- nautobot/extras/api/views.py +11 -3
- nautobot/extras/constants.py +1 -0
- nautobot/extras/datasources/git.py +125 -0
- nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
- nautobot/extras/models/customfields.py +29 -12
- nautobot/extras/models/datasources.py +85 -0
- nautobot/extras/models/models.py +15 -0
- nautobot/extras/models/relationships.py +17 -5
- nautobot/extras/signals.py +15 -1
- nautobot/extras/templates/extras/computedfield.html +1 -1
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/customfield.html +1 -1
- nautobot/extras/templates/extras/customlink.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/exporttemplate.html +1 -1
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/graphqlquery.html +1 -1
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
- nautobot/extras/templates/extras/jobhook.html +1 -1
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/plugin_detail.html +1 -1
- nautobot/extras/templates/extras/relationship.html +1 -63
- nautobot/extras/templates/extras/role_retrieve.html +1 -1
- nautobot/extras/templates/extras/scheduledjob.html +1 -1
- nautobot/extras/templates/extras/secret.html +1 -1
- nautobot/extras/templates/extras/secretsgroup.html +1 -1
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/webhook.html +1 -1
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
- nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
- nautobot/extras/tests/git_helper.py +9 -1
- nautobot/extras/tests/integration/__init__.py +29 -16
- nautobot/extras/tests/test_api.py +6 -0
- nautobot/extras/tests/test_customfields.py +49 -51
- nautobot/extras/tests/test_datasources.py +27 -0
- nautobot/extras/tests/test_models.py +283 -0
- nautobot/extras/tests/test_utils.py +22 -1
- nautobot/extras/utils.py +17 -8
- nautobot/extras/views.py +55 -12
- nautobot/ipam/models.py +8 -2
- nautobot/ipam/tables.py +2 -2
- nautobot/ipam/templates/ipam/ipaddress.html +1 -1
- nautobot/ipam/templates/ipam/prefix.html +1 -1
- nautobot/ipam/templates/ipam/rir.html +1 -1
- nautobot/ipam/templates/ipam/routetarget.html +1 -1
- nautobot/ipam/templates/ipam/service.html +1 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlangroup.html +1 -1
- nautobot/ipam/templates/ipam/vrf.html +1 -1
- nautobot/ipam/tests/test_models.py +24 -0
- nautobot/ipam/tests/test_utils.py +41 -2
- nautobot/ipam/utils/__init__.py +18 -11
- nautobot/project-static/docs/404.html +87 -12
- nautobot/project-static/docs/apps/index.html +87 -12
- nautobot/project-static/docs/apps/nautobot-apps.html +87 -12
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
- nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/graphql.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
- nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
- nautobot/project-static/docs/development/apps/api/setup.html +87 -12
- nautobot/project-static/docs/development/apps/api/testing.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
- nautobot/project-static/docs/development/apps/index.html +87 -12
- nautobot/project-static/docs/development/apps/migration/code-updates.html +87 -12
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +87 -12
- nautobot/project-static/docs/development/apps/migration/from-v1.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
- nautobot/project-static/docs/development/core/application-registry.html +87 -12
- nautobot/project-static/docs/development/core/best-practices.html +87 -12
- nautobot/project-static/docs/development/core/bootstrap-ui.html +87 -12
- nautobot/project-static/docs/development/core/caching.html +87 -12
- nautobot/project-static/docs/development/core/controllers.html +87 -12
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +87 -12
- nautobot/project-static/docs/development/core/generic-views.html +87 -12
- nautobot/project-static/docs/development/core/getting-started.html +87 -12
- nautobot/project-static/docs/development/core/homepage.html +87 -12
- nautobot/project-static/docs/development/core/index.html +87 -12
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +87 -12
- nautobot/project-static/docs/development/core/model-checklist.html +87 -12
- nautobot/project-static/docs/development/core/model-features.html +87 -12
- nautobot/project-static/docs/development/core/natural-keys.html +87 -12
- nautobot/project-static/docs/development/core/navigation-menu.html +87 -12
- nautobot/project-static/docs/development/core/release-checklist.html +87 -12
- nautobot/project-static/docs/development/core/role-internals.html +87 -12
- nautobot/project-static/docs/development/core/settings.html +87 -12
- nautobot/project-static/docs/development/core/style-guide.html +87 -12
- nautobot/project-static/docs/development/core/templates.html +88 -13
- nautobot/project-static/docs/development/core/testing.html +87 -12
- nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
- nautobot/project-static/docs/development/core/user-preferences.html +87 -12
- nautobot/project-static/docs/development/index.html +87 -12
- nautobot/project-static/docs/development/jobs/index.html +87 -12
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +87 -12
- nautobot/project-static/docs/index.html +87 -12
- nautobot/project-static/docs/overview/application_stack.html +87 -12
- nautobot/project-static/docs/overview/design_philosophy.html +87 -12
- nautobot/project-static/docs/release-notes/index.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.0.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.1.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.2.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.3.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.4.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.5.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.6.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.0.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.1.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.2.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.3.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.4.html +277 -12
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +296 -288
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +90 -15
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/services.html +87 -12
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
- nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
- nautobot/project-static/docs/user-guide/administration/security/notices.html +9843 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +187 -29
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
- nautobot/project-static/docs/user-guide/index.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
- nautobot/tenancy/templates/tenancy/tenant.html +1 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- nautobot/virtualization/templates/virtualization/cluster.html +1 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
- nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/METADATA +5 -5
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/RECORD +404 -397
- nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
- nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
- /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/NOTICE +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/WHEEL +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/entry_points.txt +0 -0
|
@@ -4,13 +4,34 @@ import uuid
|
|
|
4
4
|
from django.core.cache import cache
|
|
5
5
|
|
|
6
6
|
from nautobot.core.testing import TestCase
|
|
7
|
+
from nautobot.dcim.models import Device, LocationType
|
|
7
8
|
from nautobot.extras.choices import JobQueueTypeChoices
|
|
8
9
|
from nautobot.extras.models import JobQueue
|
|
9
10
|
from nautobot.extras.registry import registry
|
|
10
|
-
from nautobot.extras.utils import
|
|
11
|
+
from nautobot.extras.utils import (
|
|
12
|
+
get_base_template,
|
|
13
|
+
get_celery_queues,
|
|
14
|
+
get_worker_count,
|
|
15
|
+
populate_model_features_registry,
|
|
16
|
+
)
|
|
17
|
+
from nautobot.users.models import Token
|
|
11
18
|
|
|
12
19
|
|
|
13
20
|
class UtilsTestCase(TestCase):
|
|
21
|
+
def test_get_base_template(self):
|
|
22
|
+
with self.subTest("explicitly specified base_template always wins"):
|
|
23
|
+
self.assertEqual(get_base_template("dcim/device/base.html", Device), "dcim/device/base.html")
|
|
24
|
+
|
|
25
|
+
with self.subTest("<model>.html wins over <model>_retrieve.html"):
|
|
26
|
+
# TODO: why do we even have both locationtype.html and locationtype_retrieve.html?
|
|
27
|
+
self.assertEqual(get_base_template(None, LocationType), "dcim/locationtype.html")
|
|
28
|
+
|
|
29
|
+
with self.subTest("<model>_retrieve.html is used if present"):
|
|
30
|
+
self.assertEqual(get_base_template(None, JobQueue), "extras/jobqueue_retrieve.html")
|
|
31
|
+
|
|
32
|
+
with self.subTest("generic/object_retrieve.html is used as a fallback"):
|
|
33
|
+
self.assertEqual(get_base_template(None, Token), "generic/object_retrieve.html")
|
|
34
|
+
|
|
14
35
|
@mock.patch("celery.app.control.Inspect.active_queues")
|
|
15
36
|
def test_get_celery_queues(self, mock_active_queues):
|
|
16
37
|
with self.subTest("No queues"):
|
nautobot/extras/utils.py
CHANGED
|
@@ -6,6 +6,7 @@ import hmac
|
|
|
6
6
|
import logging
|
|
7
7
|
import re
|
|
8
8
|
import sys
|
|
9
|
+
from typing import Optional
|
|
9
10
|
|
|
10
11
|
from django.apps import apps
|
|
11
12
|
from django.conf import settings
|
|
@@ -13,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
13
14
|
from django.core.cache import cache
|
|
14
15
|
from django.core.validators import ValidationError
|
|
15
16
|
from django.db import transaction
|
|
16
|
-
from django.db.models import Q
|
|
17
|
+
from django.db.models import Model, Q
|
|
17
18
|
from django.template.loader import get_template, TemplateDoesNotExist
|
|
18
19
|
from django.utils.deconstruct import deconstructible
|
|
19
20
|
import kubernetes.client
|
|
@@ -36,16 +37,24 @@ from nautobot.extras.registry import registry
|
|
|
36
37
|
logger = logging.getLogger(__name__)
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
def get_base_template(base_template, model):
|
|
40
|
+
def get_base_template(base_template: Optional[str], model: type[Model]) -> str:
|
|
40
41
|
"""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
Attempt to locate the correct base template for an object detail view and related views, if one was not specified.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
base_template (str, optional): If not None, this explicitly specified template will be preferred.
|
|
46
|
+
model (Model): The model to identify a base template for, if base_template is None.
|
|
47
|
+
|
|
48
|
+
Returns the specified `base_template`, if not `None`.
|
|
49
|
+
Otherwise, if `"<app>/<model_name>.html"` exists (legacy ObjectView pattern), returns that string.
|
|
50
|
+
Otherwise, if `"<app>/<model_name>_retrieve.html"` exists (as used in `NautobotUIViewSet`), returns that string.
|
|
51
|
+
If all else fails, returns `"generic/object_retrieve.html"`.
|
|
52
|
+
|
|
53
|
+
Note: before Nautobot 2.4.2, this API would default to "base.html" rather than "generic/object_retrieve.html".
|
|
54
|
+
This behavior was changed to the current behavior to address issue #6550 and similar incorrect behavior.
|
|
45
55
|
"""
|
|
46
56
|
if base_template is None:
|
|
47
57
|
base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
|
|
48
|
-
# 2.0 TODO(Hanlin): This can be removed once an object view has been established for every model.
|
|
49
58
|
try:
|
|
50
59
|
get_template(base_template)
|
|
51
60
|
except TemplateDoesNotExist:
|
|
@@ -53,7 +62,7 @@ def get_base_template(base_template, model):
|
|
|
53
62
|
try:
|
|
54
63
|
get_template(base_template)
|
|
55
64
|
except TemplateDoesNotExist:
|
|
56
|
-
base_template = "
|
|
65
|
+
base_template = "generic/object_retrieve.html"
|
|
57
66
|
return base_template
|
|
58
67
|
|
|
59
68
|
|
nautobot/extras/views.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing import Optional
|
|
2
3
|
from urllib.parse import parse_qs
|
|
3
4
|
|
|
4
5
|
from django.contrib import messages
|
|
@@ -31,6 +32,7 @@ from nautobot.core.models.utils import pretty_print_query, serialize_object_v2
|
|
|
31
32
|
from nautobot.core.tables import ButtonsColumn
|
|
32
33
|
from nautobot.core.ui import object_detail
|
|
33
34
|
from nautobot.core.ui.choices import SectionChoices
|
|
35
|
+
from nautobot.core.ui.object_detail import ObjectDetailContent, ObjectFieldsPanel
|
|
34
36
|
from nautobot.core.utils.config import get_settings_or_config
|
|
35
37
|
from nautobot.core.utils.lookup import (
|
|
36
38
|
get_filterset_for_model,
|
|
@@ -900,10 +902,13 @@ class DynamicGroupBulkDeleteView(generic.BulkDeleteView):
|
|
|
900
902
|
class ObjectDynamicGroupsView(generic.GenericView):
|
|
901
903
|
"""
|
|
902
904
|
Present a list of dynamic groups associated to a particular object.
|
|
903
|
-
|
|
905
|
+
|
|
906
|
+
base_template: Specify to explicitly identify the base object detail template to render.
|
|
907
|
+
If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
|
|
908
|
+
will be used, as per `get_base_template()`.
|
|
904
909
|
"""
|
|
905
910
|
|
|
906
|
-
base_template = None
|
|
911
|
+
base_template: Optional[str] = None
|
|
907
912
|
|
|
908
913
|
def get(self, request, model, **kwargs):
|
|
909
914
|
# Handle QuerySet restriction of parent object if needed
|
|
@@ -926,7 +931,7 @@ class ObjectDynamicGroupsView(generic.GenericView):
|
|
|
926
931
|
}
|
|
927
932
|
RequestConfig(request, paginate).configure(dynamicgroups_table)
|
|
928
933
|
|
|
929
|
-
|
|
934
|
+
base_template = get_base_template(self.base_template, model)
|
|
930
935
|
|
|
931
936
|
return render(
|
|
932
937
|
request,
|
|
@@ -936,7 +941,7 @@ class ObjectDynamicGroupsView(generic.GenericView):
|
|
|
936
941
|
"verbose_name": obj._meta.verbose_name,
|
|
937
942
|
"verbose_name_plural": obj._meta.verbose_name_plural,
|
|
938
943
|
"table": dynamicgroups_table,
|
|
939
|
-
"base_template":
|
|
944
|
+
"base_template": base_template,
|
|
940
945
|
"active_tab": "dynamic-groups",
|
|
941
946
|
},
|
|
942
947
|
)
|
|
@@ -2185,10 +2190,13 @@ class ObjectChangeView(generic.ObjectView):
|
|
|
2185
2190
|
class ObjectChangeLogView(generic.GenericView):
|
|
2186
2191
|
"""
|
|
2187
2192
|
Present a history of changes made to a particular object.
|
|
2188
|
-
|
|
2193
|
+
|
|
2194
|
+
base_template: Specify to explicitly identify the base object detail template to render.
|
|
2195
|
+
If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
|
|
2196
|
+
will be used, as per `get_base_template()`.
|
|
2189
2197
|
"""
|
|
2190
2198
|
|
|
2191
|
-
base_template = None
|
|
2199
|
+
base_template: Optional[str] = None
|
|
2192
2200
|
|
|
2193
2201
|
def get(self, request, model, **kwargs):
|
|
2194
2202
|
# Handle QuerySet restriction of parent object if needed
|
|
@@ -2216,7 +2224,7 @@ class ObjectChangeLogView(generic.GenericView):
|
|
|
2216
2224
|
}
|
|
2217
2225
|
RequestConfig(request, paginate).configure(objectchanges_table)
|
|
2218
2226
|
|
|
2219
|
-
|
|
2227
|
+
base_template = get_base_template(self.base_template, model)
|
|
2220
2228
|
|
|
2221
2229
|
return render(
|
|
2222
2230
|
request,
|
|
@@ -2226,7 +2234,7 @@ class ObjectChangeLogView(generic.GenericView):
|
|
|
2226
2234
|
"verbose_name": obj._meta.verbose_name,
|
|
2227
2235
|
"verbose_name_plural": obj._meta.verbose_name_plural,
|
|
2228
2236
|
"table": objectchanges_table,
|
|
2229
|
-
"base_template":
|
|
2237
|
+
"base_template": base_template,
|
|
2230
2238
|
"active_tab": "changelog",
|
|
2231
2239
|
},
|
|
2232
2240
|
)
|
|
@@ -2319,10 +2327,13 @@ class NoteDeleteView(generic.ObjectDeleteView):
|
|
|
2319
2327
|
class ObjectNotesView(generic.GenericView):
|
|
2320
2328
|
"""
|
|
2321
2329
|
Present a list of notes associated to a particular object.
|
|
2322
|
-
|
|
2330
|
+
|
|
2331
|
+
base_template: Specify to explicitly identify the base object detail template to render.
|
|
2332
|
+
If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
|
|
2333
|
+
will be used, as per `get_base_template()`.
|
|
2323
2334
|
"""
|
|
2324
2335
|
|
|
2325
|
-
base_template = None
|
|
2336
|
+
base_template: Optional[str] = None
|
|
2326
2337
|
|
|
2327
2338
|
def get(self, request, model, **kwargs):
|
|
2328
2339
|
# Handle QuerySet restriction of parent object if needed
|
|
@@ -2346,7 +2357,7 @@ class ObjectNotesView(generic.GenericView):
|
|
|
2346
2357
|
}
|
|
2347
2358
|
RequestConfig(request, paginate).configure(notes_table)
|
|
2348
2359
|
|
|
2349
|
-
|
|
2360
|
+
base_template = get_base_template(self.base_template, model)
|
|
2350
2361
|
|
|
2351
2362
|
return render(
|
|
2352
2363
|
request,
|
|
@@ -2356,7 +2367,7 @@ class ObjectNotesView(generic.GenericView):
|
|
|
2356
2367
|
"verbose_name": obj._meta.verbose_name,
|
|
2357
2368
|
"verbose_name_plural": obj._meta.verbose_name_plural,
|
|
2358
2369
|
"table": notes_table,
|
|
2359
|
-
"base_template":
|
|
2370
|
+
"base_template": base_template,
|
|
2360
2371
|
"active_tab": "notes",
|
|
2361
2372
|
"form": notes_form,
|
|
2362
2373
|
},
|
|
@@ -2378,6 +2389,38 @@ class RelationshipListView(generic.ObjectListView):
|
|
|
2378
2389
|
|
|
2379
2390
|
class RelationshipView(generic.ObjectView):
|
|
2380
2391
|
queryset = Relationship.objects.all()
|
|
2392
|
+
object_detail_content = ObjectDetailContent(
|
|
2393
|
+
panels=(
|
|
2394
|
+
ObjectFieldsPanel(
|
|
2395
|
+
label="Relationship",
|
|
2396
|
+
section=SectionChoices.LEFT_HALF,
|
|
2397
|
+
weight=100,
|
|
2398
|
+
fields="__all__",
|
|
2399
|
+
exclude_fields=[
|
|
2400
|
+
"source_type",
|
|
2401
|
+
"source_label",
|
|
2402
|
+
"source_hidden",
|
|
2403
|
+
"source_filter",
|
|
2404
|
+
"destination_type",
|
|
2405
|
+
"destination_label",
|
|
2406
|
+
"destination_hidden",
|
|
2407
|
+
"destination_filter",
|
|
2408
|
+
],
|
|
2409
|
+
),
|
|
2410
|
+
ObjectFieldsPanel(
|
|
2411
|
+
label="Source Attributes",
|
|
2412
|
+
section=SectionChoices.RIGHT_HALF,
|
|
2413
|
+
weight=100,
|
|
2414
|
+
fields=["source_type", "source_label", "source_hidden", "source_filter"],
|
|
2415
|
+
),
|
|
2416
|
+
ObjectFieldsPanel(
|
|
2417
|
+
label="Destination Attributes",
|
|
2418
|
+
section=SectionChoices.RIGHT_HALF,
|
|
2419
|
+
weight=200,
|
|
2420
|
+
fields=["destination_type", "destination_label", "destination_hidden", "destination_filter"],
|
|
2421
|
+
),
|
|
2422
|
+
)
|
|
2423
|
+
)
|
|
2381
2424
|
|
|
2382
2425
|
|
|
2383
2426
|
class RelationshipEditView(generic.ObjectEditView):
|
nautobot/ipam/models.py
CHANGED
|
@@ -975,7 +975,10 @@ class Prefix(PrimaryModel):
|
|
|
975
975
|
while `<Prefix 10.0.0.0/16>.get_all_ips()` will return *both* 10.0.0.1.24 and 10.0.1.1/24.
|
|
976
976
|
"""
|
|
977
977
|
return IPAddress.objects.filter(
|
|
978
|
-
parent__namespace=self.namespace,
|
|
978
|
+
parent__namespace=self.namespace,
|
|
979
|
+
ip_version=self.ip_version,
|
|
980
|
+
host__gte=self.network,
|
|
981
|
+
host__lte=self.broadcast,
|
|
979
982
|
)
|
|
980
983
|
|
|
981
984
|
def get_first_available_prefix(self):
|
|
@@ -1016,7 +1019,10 @@ class Prefix(PrimaryModel):
|
|
|
1016
1019
|
# change this when that is the case, see #3873 for historical context.
|
|
1017
1020
|
if self.type != choices.PrefixTypeChoices.TYPE_CONTAINER:
|
|
1018
1021
|
pool_ips = IPAddress.objects.filter(
|
|
1019
|
-
parent__namespace=self.namespace,
|
|
1022
|
+
parent__namespace=self.namespace,
|
|
1023
|
+
ip_version=self.ip_version,
|
|
1024
|
+
host__gte=self.network,
|
|
1025
|
+
host__lte=self.broadcast,
|
|
1020
1026
|
).values_list("host", flat=True)
|
|
1021
1027
|
child_ips = netaddr.IPSet(pool_ips)
|
|
1022
1028
|
|
nautobot/ipam/tables.py
CHANGED
|
@@ -79,7 +79,7 @@ IPADDRESS_LINK = """
|
|
|
79
79
|
{% elif perms.ipam.add_ipaddress %}
|
|
80
80
|
<a href="\
|
|
81
81
|
{% url 'ipam:ipaddress_add' %}\
|
|
82
|
-
?address={{ record.1 }}\
|
|
82
|
+
?address={{ record.1 }}&namespace={{ object.namespace.pk }}\
|
|
83
83
|
{% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}\
|
|
84
84
|
{% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}\
|
|
85
85
|
" class="btn btn-xs btn-success">\
|
|
@@ -101,7 +101,7 @@ IPADDRESS_COPY_LINK = """
|
|
|
101
101
|
{% elif perms.ipam.add_ipaddress %}
|
|
102
102
|
<a href="\
|
|
103
103
|
{% url 'ipam:ipaddress_add' %}\
|
|
104
|
-
?address={{ record.1 }}\
|
|
104
|
+
?address={{ record.1 }}&namespace={{ object.namespace.pk }}\
|
|
105
105
|
{% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}\
|
|
106
106
|
{% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}\
|
|
107
107
|
" class="btn btn-xs btn-success">\
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
@@ -827,6 +827,24 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
827
827
|
|
|
828
828
|
self.assertEqual(parent_prefix.get_first_available_ip(), "10.0.3.2/29")
|
|
829
829
|
|
|
830
|
+
def test_get_all_ips_issue_3319(self):
|
|
831
|
+
# https://github.com/nautobot/nautobot/issues/3319
|
|
832
|
+
# Confirm that IPv4 addresses aren't caught up in the IPv6 ::/96 subnet by accident, and vice versa.
|
|
833
|
+
prefix_v6 = Prefix.objects.create(
|
|
834
|
+
prefix="::/0", type=PrefixTypeChoices.TYPE_CONTAINER, status=self.status, namespace=self.namespace
|
|
835
|
+
)
|
|
836
|
+
prefix_v4 = Prefix.objects.create(
|
|
837
|
+
prefix="0.0.0.0/0", type=PrefixTypeChoices.TYPE_CONTAINER, status=self.status, namespace=self.namespace
|
|
838
|
+
)
|
|
839
|
+
IPAddress.objects.create(address="::0102:0304/128", status=self.status, namespace=self.namespace)
|
|
840
|
+
IPAddress.objects.create(address="1.2.3.4/32", status=self.status, namespace=self.namespace)
|
|
841
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
842
|
+
prefix_v6.get_all_ips(), IPAddress.objects.filter(ip_version=6, parent__namespace=self.namespace)
|
|
843
|
+
)
|
|
844
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
845
|
+
prefix_v4.get_all_ips(), IPAddress.objects.filter(ip_version=4, parent__namespace=self.namespace)
|
|
846
|
+
)
|
|
847
|
+
|
|
830
848
|
def test_get_utilization(self):
|
|
831
849
|
# Container Prefix
|
|
832
850
|
prefix = Prefix.objects.create(
|
|
@@ -982,6 +1000,12 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
982
1000
|
Prefix.objects.create(prefix="ab80::/9", status=self.status, namespace=self.namespace)
|
|
983
1001
|
self.assertEqual(large_prefix_v6.get_utilization(), (2**120, 2**120))
|
|
984
1002
|
|
|
1003
|
+
# https://github.com/nautobot/nautobot/issues/3319
|
|
1004
|
+
v4_10dot_address_space_in_v6 = Prefix.objects.create(
|
|
1005
|
+
prefix="0a00::/8", type=PrefixTypeChoices.TYPE_NETWORK, status=self.status, namespace=self.namespace
|
|
1006
|
+
)
|
|
1007
|
+
self.assertSequenceEqual(v4_10dot_address_space_in_v6.get_utilization(), (0, 2**120))
|
|
1008
|
+
|
|
985
1009
|
#
|
|
986
1010
|
# Uniqueness enforcement tests
|
|
987
1011
|
#
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from django.test import TestCase
|
|
2
|
+
import netaddr
|
|
2
3
|
|
|
3
4
|
from nautobot.core.forms.utils import parse_numeric_range
|
|
4
5
|
from nautobot.extras.models import Status
|
|
5
|
-
from nautobot.ipam.models import VLAN, VLANGroup
|
|
6
|
-
from nautobot.ipam.utils import add_available_vlans
|
|
6
|
+
from nautobot.ipam.models import IPAddress, Namespace, Prefix, VLAN, VLANGroup
|
|
7
|
+
from nautobot.ipam.utils import add_available_ipaddresses, add_available_vlans
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class AddAvailableVlansTest(TestCase):
|
|
@@ -27,6 +28,44 @@ class AddAvailableVlansTest(TestCase):
|
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
class AddAvailableIPsTest(TestCase):
|
|
32
|
+
"""Tests for add_available_ipaddresses()."""
|
|
33
|
+
|
|
34
|
+
def test_add_available_ipaddresses_ipv4(self):
|
|
35
|
+
prefix = Prefix.objects.create(prefix="22.22.22.0/24", status=Status.objects.get_for_model(Prefix).first())
|
|
36
|
+
ip_status = Status.objects.get_for_model(IPAddress).first()
|
|
37
|
+
# .0 isn't available since this isn't a Pool prefix
|
|
38
|
+
available_1 = (9, "22.22.22.1/24")
|
|
39
|
+
ip_1 = IPAddress.objects.create(address="22.22.22.10/24", status=ip_status)
|
|
40
|
+
available_2 = (10, "22.22.22.11/24")
|
|
41
|
+
ip_2 = IPAddress.objects.create(address="22.22.22.21/24", status=ip_status)
|
|
42
|
+
available_3 = (233, "22.22.22.22/24")
|
|
43
|
+
# .255 isn't available since this isn't a Pool prefix
|
|
44
|
+
self.assertEqual(
|
|
45
|
+
add_available_ipaddresses(prefix=netaddr.IPNetwork(prefix.prefix), ipaddress_list=(ip_1, ip_2)),
|
|
46
|
+
[available_1, ip_1, available_2, ip_2, available_3],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def test_add_available_ipaddresses_ipv6(self):
|
|
50
|
+
namespace = Namespace.objects.create(name="add_available_ipv6")
|
|
51
|
+
prefix = Prefix.objects.create(
|
|
52
|
+
prefix="::/0", status=Status.objects.get_for_model(Prefix).first(), namespace=namespace
|
|
53
|
+
)
|
|
54
|
+
ip_status = Status.objects.get_for_model(IPAddress).first()
|
|
55
|
+
# .0 is available in IPv6
|
|
56
|
+
available_1 = (10, "::/0")
|
|
57
|
+
ip_1 = IPAddress.objects.create(address="::a/0", status=ip_status, namespace=namespace)
|
|
58
|
+
available_2 = (2**128 - 10 - 10 - 2, "::b/0")
|
|
59
|
+
ip_2 = IPAddress.objects.create(
|
|
60
|
+
address="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff5/0", status=ip_status, namespace=namespace
|
|
61
|
+
)
|
|
62
|
+
available_3 = (10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff6/0")
|
|
63
|
+
self.assertEqual(
|
|
64
|
+
add_available_ipaddresses(prefix=netaddr.IPNetwork(prefix.prefix), ipaddress_list=(ip_1, ip_2)),
|
|
65
|
+
[available_1, ip_1, available_2, ip_2, available_3],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
30
69
|
class ParseNumericRangeTest(TestCase):
|
|
31
70
|
"""Tests for add_available_vlans()."""
|
|
32
71
|
|
nautobot/ipam/utils/__init__.py
CHANGED
|
@@ -41,20 +41,27 @@ def get_add_available_prefixes_callback(show_available: bool, parent: Prefix):
|
|
|
41
41
|
|
|
42
42
|
def add_available_ipaddresses(prefix: netaddr.IPNetwork, ipaddress_list: Iterable[IPAddress], is_pool: bool = False):
|
|
43
43
|
"""
|
|
44
|
-
Annotate ranges of available IP addresses within a given prefix.
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
Annotate ranges of available IP addresses within a given prefix.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
prefix (netaddr.IPNetwork): The network to calculate available addresses within.
|
|
48
|
+
ipaddress_list (Iterable[IPAddress]): List or QuerySet of extant IPAddress objects.
|
|
49
|
+
is_pool (bool): If True, the first/last IPs in the prefix will be considered usable, regardless of mask length.
|
|
47
50
|
|
|
51
|
+
Returns:
|
|
52
|
+
The contents of `ipaddress_list` interleaved with tuples of the form
|
|
53
|
+
`(number_of_available_addresses, first_such_address)`.
|
|
54
|
+
"""
|
|
48
55
|
output = []
|
|
49
56
|
prev_ip = None
|
|
50
57
|
|
|
51
58
|
# Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31.
|
|
52
59
|
if prefix.version == 4 and prefix.prefixlen < 31 and not is_pool:
|
|
53
|
-
first_ip_in_prefix = netaddr.IPAddress(prefix.first + 1)
|
|
54
|
-
last_ip_in_prefix = netaddr.IPAddress(prefix.last - 1)
|
|
60
|
+
first_ip_in_prefix = netaddr.IPAddress(prefix.first + 1, version=prefix.version)
|
|
61
|
+
last_ip_in_prefix = netaddr.IPAddress(prefix.last - 1, version=prefix.version)
|
|
55
62
|
else:
|
|
56
|
-
first_ip_in_prefix = netaddr.IPAddress(prefix.first)
|
|
57
|
-
last_ip_in_prefix = netaddr.IPAddress(prefix.last)
|
|
63
|
+
first_ip_in_prefix = netaddr.IPAddress(prefix.first, version=prefix.version)
|
|
64
|
+
last_ip_in_prefix = netaddr.IPAddress(prefix.last, version=prefix.version)
|
|
58
65
|
|
|
59
66
|
if not ipaddress_list:
|
|
60
67
|
return [
|
|
@@ -71,15 +78,15 @@ def add_available_ipaddresses(prefix: netaddr.IPNetwork, ipaddress_list: Iterabl
|
|
|
71
78
|
ipaddress_list.sort(key=lambda ip: ip.host)
|
|
72
79
|
|
|
73
80
|
# Account for any available IPs before the first real IP
|
|
74
|
-
if ipaddress_list[0].address.ip > first_ip_in_prefix:
|
|
75
|
-
skipped_count =
|
|
81
|
+
if ipaddress_list[0].address.ip.value > first_ip_in_prefix.value:
|
|
82
|
+
skipped_count = ipaddress_list[0].address.ip.value - first_ip_in_prefix.value
|
|
76
83
|
first_skipped = f"{first_ip_in_prefix}/{prefix.prefixlen}"
|
|
77
84
|
output.append((skipped_count, first_skipped))
|
|
78
85
|
|
|
79
86
|
# Iterate through existing IPs and annotate free ranges
|
|
80
87
|
for ip in ipaddress_list:
|
|
81
88
|
if prev_ip:
|
|
82
|
-
diff =
|
|
89
|
+
diff = ip.address.ip.value - prev_ip.address.ip.value
|
|
83
90
|
if diff > 1:
|
|
84
91
|
first_skipped = f"{prev_ip.address.ip + 1}/{prefix.prefixlen}"
|
|
85
92
|
output.append((diff - 1, first_skipped))
|
|
@@ -88,7 +95,7 @@ def add_available_ipaddresses(prefix: netaddr.IPNetwork, ipaddress_list: Iterabl
|
|
|
88
95
|
|
|
89
96
|
# Include any remaining available IPs
|
|
90
97
|
if prev_ip.address.ip < last_ip_in_prefix:
|
|
91
|
-
skipped_count =
|
|
98
|
+
skipped_count = last_ip_in_prefix.value - prev_ip.address.ip.value
|
|
92
99
|
first_skipped = f"{prev_ip.address.ip + 1}/{prefix.prefixlen}"
|
|
93
100
|
output.append((skipped_count, first_skipped))
|
|
94
101
|
|