nautobot 2.3.12__py3-none-any.whl → 2.3.13__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/core/api/serializers.py +1 -0
- nautobot/core/celery/log.py +4 -4
- nautobot/core/models/tree_queries.py +5 -2
- nautobot/core/settings.py +1 -1
- nautobot/core/tables.py +60 -10
- nautobot/core/templatetags/helpers.py +7 -1
- nautobot/core/testing/api.py +5 -1
- nautobot/core/tests/test_api.py +20 -0
- nautobot/core/tests/test_csv.py +25 -3
- nautobot/core/tests/test_utils.py +8 -0
- nautobot/core/utils/lookup.py +11 -8
- nautobot/dcim/api/views.py +3 -0
- nautobot/dcim/filters/__init__.py +26 -1
- nautobot/dcim/forms.py +4 -0
- nautobot/dcim/tests/test_filters.py +33 -0
- nautobot/dcim/tests/test_views.py +6 -0
- nautobot/extras/api/serializers.py +1 -0
- nautobot/extras/api/views.py +2 -0
- nautobot/extras/forms/forms.py +2 -0
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/plugins/__init__.py +13 -2
- nautobot/extras/tests/test_views.py +2 -0
- nautobot/ipam/lookups.py +101 -62
- nautobot/ipam/tables.py +18 -4
- nautobot/ipam/tests/test_querysets.py +49 -1
- nautobot/ipam/utils/__init__.py +24 -0
- nautobot/ipam/views.py +61 -68
- nautobot/project-static/docs/404.html +1 -1
- nautobot/project-static/docs/apps/index.html +1 -1
- nautobot/project-static/docs/apps/nautobot-apps.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +197 -5
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +16 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1 -1
- nautobot/project-static/docs/development/apps/api/configuration-view.html +1 -1
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +1 -1
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +1 -1
- nautobot/project-static/docs/development/apps/api/models/global-search.html +1 -1
- nautobot/project-static/docs/development/apps/api/models/graphql.html +1 -1
- nautobot/project-static/docs/development/apps/api/models/index.html +1 -1
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +1 -1
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +23 -4
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +1 -1
- nautobot/project-static/docs/development/apps/api/prometheus.html +1 -1
- nautobot/project-static/docs/development/apps/api/setup.html +1 -1
- nautobot/project-static/docs/development/apps/api/testing.html +1 -1
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +1 -1
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +1 -1
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +1 -1
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +1 -1
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/base-template.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/index.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/notes.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +1 -1
- nautobot/project-static/docs/development/apps/api/views/urls.html +1 -1
- nautobot/project-static/docs/development/apps/index.html +1 -1
- nautobot/project-static/docs/development/apps/migration/code-updates.html +1 -1
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +1 -1
- nautobot/project-static/docs/development/apps/migration/from-v1.html +1 -1
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +1 -1
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +1 -1
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +1 -1
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +1 -1
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +1 -1
- nautobot/project-static/docs/development/core/application-registry.html +1 -1
- nautobot/project-static/docs/development/core/best-practices.html +1 -1
- nautobot/project-static/docs/development/core/bootstrap-ui.html +1 -1
- nautobot/project-static/docs/development/core/caching.html +1 -1
- nautobot/project-static/docs/development/core/controllers.html +1 -1
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +1 -1
- nautobot/project-static/docs/development/core/generic-views.html +1 -1
- nautobot/project-static/docs/development/core/getting-started.html +1 -1
- nautobot/project-static/docs/development/core/homepage.html +1 -1
- nautobot/project-static/docs/development/core/index.html +1 -1
- nautobot/project-static/docs/development/core/model-checklist.html +1 -1
- nautobot/project-static/docs/development/core/model-features.html +1 -1
- nautobot/project-static/docs/development/core/natural-keys.html +1 -1
- nautobot/project-static/docs/development/core/navigation-menu.html +1 -1
- nautobot/project-static/docs/development/core/release-checklist.html +1 -1
- nautobot/project-static/docs/development/core/role-internals.html +1 -1
- nautobot/project-static/docs/development/core/settings.html +1 -1
- nautobot/project-static/docs/development/core/style-guide.html +1 -1
- nautobot/project-static/docs/development/core/templates.html +1 -1
- nautobot/project-static/docs/development/core/testing.html +1 -1
- nautobot/project-static/docs/development/core/user-preferences.html +1 -1
- nautobot/project-static/docs/development/index.html +1 -1
- nautobot/project-static/docs/development/jobs/index.html +1 -1
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +1 -1
- nautobot/project-static/docs/index.html +1 -1
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +1 -1
- nautobot/project-static/docs/overview/design_philosophy.html +1 -1
- nautobot/project-static/docs/release-notes/index.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.0.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.1.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.2.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.3.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.4.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.5.html +1 -1
- nautobot/project-static/docs/release-notes/version-1.6.html +614 -179
- nautobot/project-static/docs/release-notes/version-2.0.html +1 -1
- nautobot/project-static/docs/release-notes/version-2.1.html +1 -1
- nautobot/project-static/docs/release-notes/version-2.2.html +1 -1
- nautobot/project-static/docs/release-notes/version-2.3.html +401 -199
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +270 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +1 -1
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +1 -1
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +3 -3
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +1 -1
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +1 -1
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +1 -1
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +1 -1
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/index.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +1 -1
- nautobot/project-static/docs/user-guide/administration/installation/services.html +1 -1
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +1 -1
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +1 -1
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +1 -1
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +1 -1
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +1 -1
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +1 -1
- nautobot/project-static/docs/user-guide/index.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +1 -1
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +1 -1
- nautobot/users/api/serializers.py +1 -0
- nautobot/virtualization/filters.py +19 -2
- nautobot/virtualization/tests/test_filters.py +9 -0
- {nautobot-2.3.12.dist-info → nautobot-2.3.13.dist-info}/METADATA +3 -3
- {nautobot-2.3.12.dist-info → nautobot-2.3.13.dist-info}/RECORD +312 -312
- {nautobot-2.3.12.dist-info → nautobot-2.3.13.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.12.dist-info → nautobot-2.3.13.dist-info}/NOTICE +0 -0
- {nautobot-2.3.12.dist-info → nautobot-2.3.13.dist-info}/WHEEL +0 -0
- {nautobot-2.3.12.dist-info → nautobot-2.3.13.dist-info}/entry_points.txt +0 -0
|
@@ -519,6 +519,7 @@ class TableExtension:
|
|
|
519
519
|
"""
|
|
520
520
|
|
|
521
521
|
model = None
|
|
522
|
+
suffix = None
|
|
522
523
|
table_columns = {}
|
|
523
524
|
add_to_default_columns = ()
|
|
524
525
|
remove_from_default_columns = ()
|
|
@@ -594,7 +595,7 @@ def _add_columns_into_model_table(table_extension, app_name):
|
|
|
594
595
|
logger.error(error)
|
|
595
596
|
return
|
|
596
597
|
|
|
597
|
-
table = get_table_for_model(table_extension.model)
|
|
598
|
+
table = get_table_for_model(table_extension.model, suffix=table_extension.suffix)
|
|
598
599
|
for name, column in table_extension.table_columns.items():
|
|
599
600
|
_validate_table_column_name_is_prefixed_with_app_name(name, app_name)
|
|
600
601
|
_add_column_to_table_base_columns(table, name, column, app_name)
|
|
@@ -629,18 +630,28 @@ def _modify_default_table_columns(table_extension, app_name):
|
|
|
629
630
|
"""Add or remove columns from the table default columns."""
|
|
630
631
|
from nautobot.core.utils.lookup import get_table_for_model
|
|
631
632
|
|
|
632
|
-
table = get_table_for_model(table_extension.model)
|
|
633
|
+
table = get_table_for_model(table_extension.model, suffix=table_extension.suffix)
|
|
633
634
|
message = (
|
|
634
635
|
f"{app_name}: Cannot {{action}} column `{{column_name}}` {{preposition}} the default columns for `{table}`."
|
|
635
636
|
)
|
|
636
637
|
|
|
637
638
|
for column_name in table_extension.add_to_default_columns:
|
|
639
|
+
if not getattr(table.Meta, "default_columns", None):
|
|
640
|
+
logger.warning(
|
|
641
|
+
f"{app_name}: Table `{table}` does not have a `default_columns` attribute. Cannot add column: {column_name}."
|
|
642
|
+
)
|
|
643
|
+
continue
|
|
638
644
|
if column_name in table.base_columns:
|
|
639
645
|
table.Meta.default_columns = (*table.Meta.default_columns, column_name)
|
|
640
646
|
else:
|
|
641
647
|
logger.debug(message.format(action="add", column_name=column_name, preposition="to"))
|
|
642
648
|
|
|
643
649
|
for column_name in table_extension.remove_from_default_columns:
|
|
650
|
+
if not getattr(table.Meta, "default_columns", None):
|
|
651
|
+
logger.warning(
|
|
652
|
+
f"{app_name}: Table `{table}` does not have a `default_columns` attribute. Cannot remove column: {column_name}."
|
|
653
|
+
)
|
|
654
|
+
continue
|
|
644
655
|
if column_name in table.Meta.default_columns:
|
|
645
656
|
table.Meta.default_columns = tuple(name for name in table.Meta.default_columns if name != column_name)
|
|
646
657
|
else:
|
|
@@ -3607,6 +3607,8 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|
|
3607
3607
|
|
|
3608
3608
|
cls.bulk_edit_data = {
|
|
3609
3609
|
"color": "000000",
|
|
3610
|
+
"description": "I used to be a new role object.",
|
|
3611
|
+
"weight": 255,
|
|
3610
3612
|
}
|
|
3611
3613
|
|
|
3612
3614
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
nautobot/ipam/lookups.py
CHANGED
|
@@ -3,7 +3,9 @@ from django.db.models import Lookup, lookups
|
|
|
3
3
|
import netaddr
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def _mysql_varbin_to_broadcast():
|
|
6
|
+
def _mysql_varbin_to_broadcast(alias=None):
|
|
7
|
+
if alias:
|
|
8
|
+
return f"HEX({alias}.broadcast)"
|
|
7
9
|
return "HEX(broadcast)"
|
|
8
10
|
|
|
9
11
|
|
|
@@ -13,11 +15,15 @@ def _mysql_varbin_to_hex(lhs, alias=None):
|
|
|
13
15
|
return f"HEX({lhs})"
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
def _mysql_varbin_to_network():
|
|
18
|
+
def _mysql_varbin_to_network(alias=None):
|
|
19
|
+
if alias:
|
|
20
|
+
return f"HEX({alias}.network)"
|
|
17
21
|
return "HEX(network)"
|
|
18
22
|
|
|
19
23
|
|
|
20
|
-
def _postgresql_varbin_to_broadcast(length):
|
|
24
|
+
def _postgresql_varbin_to_broadcast(length, alias=None):
|
|
25
|
+
if alias:
|
|
26
|
+
return f"right({alias}.broadcast::text, -1)::varbit::bit({length})"
|
|
21
27
|
return f"right(broadcast::text, -1)::varbit::bit({length})"
|
|
22
28
|
|
|
23
29
|
|
|
@@ -27,8 +33,10 @@ def _postgresql_varbin_to_integer(lhs, length, alias=None):
|
|
|
27
33
|
return f"right({lhs}::text, -1)::varbit::bit({length})"
|
|
28
34
|
|
|
29
35
|
|
|
30
|
-
def _postgresql_varbin_to_network(lhs, length):
|
|
36
|
+
def _postgresql_varbin_to_network(lhs, length, alias=None):
|
|
31
37
|
# convert to bitstring, 0 out everything larger than prefix_length
|
|
38
|
+
if alias:
|
|
39
|
+
return f"lpad(right({alias}.{lhs}::text, -1)::varbit::text, {alias}.prefix_length, '0')::bit({length})"
|
|
32
40
|
return f"lpad(right({lhs}::text, -1)::varbit::text, prefix_length, '0')::bit({length})"
|
|
33
41
|
|
|
34
42
|
|
|
@@ -52,8 +60,8 @@ def get_ip_info(field_name, ip_str, alias=None):
|
|
|
52
60
|
ip_details.rhs = py_to_hex(ip.ip, ip_details.length)
|
|
53
61
|
ip_details.net_addr = f"'{py_to_hex(ip.network, ip_details.length)}'"
|
|
54
62
|
ip_details.bcast_addr = f"'{py_to_hex(ip[-1], ip_details.length)}'"
|
|
55
|
-
ip_details.q_net = _mysql_varbin_to_network()
|
|
56
|
-
ip_details.q_bcast = _mysql_varbin_to_broadcast()
|
|
63
|
+
ip_details.q_net = _mysql_varbin_to_network(alias=alias)
|
|
64
|
+
ip_details.q_bcast = _mysql_varbin_to_broadcast(alias=alias)
|
|
57
65
|
ip_details.q_ip = _mysql_varbin_to_hex(field_name, alias=alias)
|
|
58
66
|
|
|
59
67
|
elif _connection.vendor == "postgresql":
|
|
@@ -61,8 +69,8 @@ def get_ip_info(field_name, ip_str, alias=None):
|
|
|
61
69
|
ip_details.addr_str = f"B'{bin(int(ip_details.addr))[2:].zfill(ip_details.length)}'"
|
|
62
70
|
ip_details.net_addr = f"B'{bin(int(ip.network))[2:].zfill(ip_details.length)}'"
|
|
63
71
|
ip_details.bcast_addr = f"B'{bin(int(ip[-1]))[2:].zfill(ip_details.length)}'"
|
|
64
|
-
ip_details.q_net = _postgresql_varbin_to_network(field_name, ip_details.length)
|
|
65
|
-
ip_details.q_bcast = _postgresql_varbin_to_broadcast(ip_details.length)
|
|
72
|
+
ip_details.q_net = _postgresql_varbin_to_network(field_name, ip_details.length, alias=alias)
|
|
73
|
+
ip_details.q_bcast = _postgresql_varbin_to_broadcast(ip_details.length, alias=alias)
|
|
66
74
|
ip_details.q_ip = _postgresql_varbin_to_integer(field_name, ip_details.length, alias=alias)
|
|
67
75
|
|
|
68
76
|
return ip_details
|
|
@@ -71,25 +79,38 @@ def get_ip_info(field_name, ip_str, alias=None):
|
|
|
71
79
|
class IPDetails:
|
|
72
80
|
"""Class for setting up all details about an IP they may be needed"""
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
q_ip = None
|
|
82
|
+
addr = None # 10.0.0.0
|
|
83
|
+
ip = None # 10.0.0.0/8
|
|
84
|
+
prefix = None # 8
|
|
85
|
+
length = None # 32
|
|
86
|
+
addr_str = None # B'00001010000000000000000000000000'
|
|
87
|
+
rhs = None # 00001010000000000000000000000000
|
|
88
|
+
net_addr = None # B'00001010000000000000000000000000'
|
|
89
|
+
bcast_addr = None # B'00001010111111111111111111111111'
|
|
90
|
+
q_net = None # mysql or postgres specific
|
|
91
|
+
q_bcast = None # mysql or postgres specific
|
|
92
|
+
q_ip = None # mysql or postgres specific
|
|
86
93
|
to_len = {4: 32, 6: 128}
|
|
87
94
|
|
|
95
|
+
def __str__(self):
|
|
96
|
+
return f"""\
|
|
97
|
+
addr: {self.addr}
|
|
98
|
+
ip: {self.ip}
|
|
99
|
+
prefix: {self.prefix}
|
|
100
|
+
length: {self.length}
|
|
101
|
+
addr_str: {self.addr_str}
|
|
102
|
+
rhs: {self.rhs}
|
|
103
|
+
net_addr: {self.net_addr}
|
|
104
|
+
bcast_addr: {self.bcast_addr}
|
|
105
|
+
q_net: {self.q_net}
|
|
106
|
+
q_bcast: {self.q_bcast}
|
|
107
|
+
q_ip: {self.q_ip}"""
|
|
108
|
+
|
|
88
109
|
|
|
89
110
|
class StringMatchMixin:
|
|
90
|
-
def process_lhs(self,
|
|
111
|
+
def process_lhs(self, compiler, connection, lhs=None):
|
|
91
112
|
lhs = lhs or self.lhs
|
|
92
|
-
lhs_string, lhs_params =
|
|
113
|
+
lhs_string, lhs_params = compiler.compile(lhs)
|
|
93
114
|
if connection.vendor == "postgresql":
|
|
94
115
|
raise NotSupportedError("Lookup not supported on postgresql.")
|
|
95
116
|
return f"INET6_NTOA({lhs_string})", lhs_params
|
|
@@ -129,6 +150,7 @@ class IRegex(StringMatchMixin, lookups.IRegex):
|
|
|
129
150
|
|
|
130
151
|
class NetworkFieldMixin:
|
|
131
152
|
def get_prep_lookup(self):
|
|
153
|
+
self.alias = self.lhs.alias
|
|
132
154
|
field_name = self.lhs.field.name
|
|
133
155
|
if field_name not in ["host", "network"]:
|
|
134
156
|
raise NotSupportedError(f"Lookup only provided on the host and network fields, not {field_name}.")
|
|
@@ -139,8 +161,8 @@ class NetworkFieldMixin:
|
|
|
139
161
|
self.ip = get_ip_info(field_name, self.rhs, alias=self.lhs.alias)
|
|
140
162
|
return str(self.ip.ip)
|
|
141
163
|
|
|
142
|
-
def process_rhs(self,
|
|
143
|
-
sql, params = super().process_rhs(
|
|
164
|
+
def process_rhs(self, compiler, connection):
|
|
165
|
+
sql, params = super().process_rhs(compiler, connection)
|
|
144
166
|
params[0] = self.ip.rhs
|
|
145
167
|
return sql, params
|
|
146
168
|
|
|
@@ -148,50 +170,67 @@ class NetworkFieldMixin:
|
|
|
148
170
|
class NetEquals(NetworkFieldMixin, Lookup):
|
|
149
171
|
lookup_name = "net_equals"
|
|
150
172
|
|
|
151
|
-
def as_sql(self,
|
|
152
|
-
_, lhs_params = self.process_lhs(
|
|
153
|
-
rhs, rhs_params = self.process_rhs(
|
|
154
|
-
|
|
173
|
+
def as_sql(self, compiler, connection):
|
|
174
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
175
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
176
|
+
if self.alias:
|
|
177
|
+
query = f"{self.alias}.prefix_length = {self.ip.prefix} AND {rhs} = {self.ip.q_ip}"
|
|
178
|
+
else:
|
|
179
|
+
query = f"prefix_length = {self.ip.prefix} AND {rhs} = {self.ip.q_ip}"
|
|
155
180
|
return query, lhs_params + rhs_params
|
|
156
181
|
|
|
157
182
|
|
|
158
183
|
class NetContainsOrEquals(NetworkFieldMixin, Lookup):
|
|
159
184
|
lookup_name = "net_contains_or_equals"
|
|
160
185
|
|
|
161
|
-
def as_sql(self,
|
|
162
|
-
_, lhs_params = self.process_lhs(
|
|
163
|
-
rhs, rhs_params = self.process_rhs(
|
|
164
|
-
|
|
186
|
+
def as_sql(self, compiler, connection):
|
|
187
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
188
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
189
|
+
if self.alias:
|
|
190
|
+
query = f"{self.alias}.prefix_length <= {self.ip.prefix} AND {rhs} BETWEEN {self.ip.q_net} AND {self.ip.q_bcast}"
|
|
191
|
+
else:
|
|
192
|
+
query = f"prefix_length <= {self.ip.prefix} AND {rhs} BETWEEN {self.ip.q_net} AND {self.ip.q_bcast}"
|
|
165
193
|
return query, lhs_params + rhs_params
|
|
166
194
|
|
|
167
195
|
|
|
168
196
|
class NetContains(NetworkFieldMixin, Lookup):
|
|
169
197
|
lookup_name = "net_contains"
|
|
170
198
|
|
|
171
|
-
def as_sql(self,
|
|
172
|
-
_, lhs_params = self.process_lhs(
|
|
173
|
-
rhs, rhs_params = self.process_rhs(
|
|
174
|
-
|
|
199
|
+
def as_sql(self, compiler, connection):
|
|
200
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
201
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
202
|
+
if self.alias:
|
|
203
|
+
query = (
|
|
204
|
+
f"{self.alias}.prefix_length < {self.ip.prefix} AND {rhs} BETWEEN {self.ip.q_net} AND {self.ip.q_bcast}"
|
|
205
|
+
)
|
|
206
|
+
else:
|
|
207
|
+
query = f"prefix_length < {self.ip.prefix} AND {rhs} BETWEEN {self.ip.q_net} AND {self.ip.q_bcast}"
|
|
175
208
|
return query, lhs_params + rhs_params
|
|
176
209
|
|
|
177
210
|
|
|
178
211
|
class NetContainedOrEqual(NetworkFieldMixin, Lookup):
|
|
179
212
|
lookup_name = "net_contained_or_equal"
|
|
180
213
|
|
|
181
|
-
def as_sql(self,
|
|
182
|
-
_, lhs_params = self.process_lhs(
|
|
183
|
-
rhs, rhs_params = self.process_rhs(
|
|
184
|
-
|
|
214
|
+
def as_sql(self, compiler, connection):
|
|
215
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
216
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
217
|
+
if self.alias:
|
|
218
|
+
query = f"{self.alias}.prefix_length >= {self.ip.prefix} AND {self.ip.q_net} BETWEEN {rhs} AND {self.ip.bcast_addr}"
|
|
219
|
+
else:
|
|
220
|
+
query = f"prefix_length >= {self.ip.prefix} AND {self.ip.q_net} BETWEEN {rhs} AND {self.ip.bcast_addr}"
|
|
185
221
|
return query, lhs_params + rhs_params
|
|
186
222
|
|
|
187
223
|
|
|
188
224
|
class NetContained(NetworkFieldMixin, Lookup):
|
|
189
225
|
lookup_name = "net_contained"
|
|
190
226
|
|
|
191
|
-
def as_sql(self,
|
|
192
|
-
_, lhs_params = self.process_lhs(
|
|
193
|
-
rhs, rhs_params = self.process_rhs(
|
|
194
|
-
|
|
227
|
+
def as_sql(self, compiler, connection):
|
|
228
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
229
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
230
|
+
if self.alias:
|
|
231
|
+
query = f"{self.alias}.prefix_length > {self.ip.prefix} AND {self.ip.q_net} BETWEEN {rhs} AND {self.ip.bcast_addr}"
|
|
232
|
+
else:
|
|
233
|
+
query = f"prefix_length > {self.ip.prefix} AND {self.ip.q_net} BETWEEN {rhs} AND {self.ip.bcast_addr}"
|
|
195
234
|
return query, lhs_params + rhs_params
|
|
196
235
|
|
|
197
236
|
|
|
@@ -205,19 +244,19 @@ class NetHost(Lookup):
|
|
|
205
244
|
self.ip = get_ip_info(field_name, self.rhs, alias=self.lhs.alias)
|
|
206
245
|
return str(self.ip.ip)
|
|
207
246
|
|
|
208
|
-
def process_rhs(self,
|
|
209
|
-
sql, params = super().process_rhs(
|
|
247
|
+
def process_rhs(self, compiler, connection):
|
|
248
|
+
sql, params = super().process_rhs(compiler, connection)
|
|
210
249
|
params[0] = self.ip.rhs
|
|
211
250
|
return sql, params
|
|
212
251
|
|
|
213
|
-
def process_lhs(self,
|
|
252
|
+
def process_lhs(self, compiler, connection, lhs=None):
|
|
214
253
|
lhs = lhs or self.lhs
|
|
215
|
-
_, lhs_params =
|
|
254
|
+
_, lhs_params = compiler.compile(lhs)
|
|
216
255
|
return self.ip.q_ip, lhs_params
|
|
217
256
|
|
|
218
|
-
def as_sql(self,
|
|
219
|
-
lhs, lhs_params = self.process_lhs(
|
|
220
|
-
rhs, rhs_params = self.process_rhs(
|
|
257
|
+
def as_sql(self, compiler, connection):
|
|
258
|
+
lhs, lhs_params = self.process_lhs(compiler, connection)
|
|
259
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
221
260
|
return f"{lhs} = {rhs}", lhs_params + rhs_params
|
|
222
261
|
|
|
223
262
|
|
|
@@ -242,9 +281,9 @@ class NetIn(Lookup):
|
|
|
242
281
|
self.query_starter = "'1' != ANY(%s) AND "
|
|
243
282
|
return self.rhs
|
|
244
283
|
|
|
245
|
-
def as_sql(self,
|
|
246
|
-
_, lhs_params = self.process_lhs(
|
|
247
|
-
_, rhs_params = self.process_rhs(
|
|
284
|
+
def as_sql(self, compiler, connection):
|
|
285
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
286
|
+
_, rhs_params = self.process_rhs(compiler, connection)
|
|
248
287
|
query = self.query_starter
|
|
249
288
|
query += "OR ".join(f"{ip.q_ip} BETWEEN {ip.net_addr} AND {ip.bcast_addr} " for ip in self.ips)
|
|
250
289
|
return query, lhs_params + rhs_params
|
|
@@ -253,9 +292,9 @@ class NetIn(Lookup):
|
|
|
253
292
|
class NetHostContained(NetworkFieldMixin, Lookup):
|
|
254
293
|
lookup_name = "net_host_contained"
|
|
255
294
|
|
|
256
|
-
def as_sql(self,
|
|
257
|
-
_, lhs_params = self.process_lhs(
|
|
258
|
-
rhs, rhs_params = self.process_rhs(
|
|
295
|
+
def as_sql(self, compiler, connection):
|
|
296
|
+
_, lhs_params = self.process_lhs(compiler, connection)
|
|
297
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
259
298
|
query = f"{self.ip.q_ip} BETWEEN {rhs} AND {self.ip.bcast_addr}"
|
|
260
299
|
return query, lhs_params + rhs_params
|
|
261
300
|
|
|
@@ -270,12 +309,12 @@ class NetFamily(Lookup):
|
|
|
270
309
|
self.rhs = 16
|
|
271
310
|
return self.rhs
|
|
272
311
|
|
|
273
|
-
def process_lhs(self,
|
|
312
|
+
def process_lhs(self, compiler, connection, lhs=None):
|
|
274
313
|
lhs = lhs or self.lhs
|
|
275
|
-
lhs_string, lhs_params =
|
|
314
|
+
lhs_string, lhs_params = compiler.compile(lhs)
|
|
276
315
|
return f"LENGTH({lhs_string})", lhs_params
|
|
277
316
|
|
|
278
|
-
def as_sql(self,
|
|
279
|
-
lhs, lhs_params = self.process_lhs(
|
|
280
|
-
rhs, rhs_params = self.process_rhs(
|
|
317
|
+
def as_sql(self, compiler, connection):
|
|
318
|
+
lhs, lhs_params = self.process_lhs(compiler, connection)
|
|
319
|
+
rhs, rhs_params = self.process_rhs(compiler, connection)
|
|
281
320
|
return f"{lhs} = {rhs}", lhs_params + rhs_params
|
nautobot/ipam/tables.py
CHANGED
|
@@ -442,13 +442,27 @@ class IPAddressTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
442
442
|
)
|
|
443
443
|
tenant = TenantColumn()
|
|
444
444
|
parent__namespace = tables.Column(linkify=True)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
445
|
+
interface_count = LinkedCountColumn(
|
|
446
|
+
viewname="dcim:interface_list", url_params={"ip_addresses": "pk"}, verbose_name="Interfaces"
|
|
447
|
+
)
|
|
448
|
+
# TODO: what about interfaces assigned to modules?
|
|
449
|
+
interface_parent_count = LinkedCountColumn(
|
|
450
|
+
viewname="dcim:device_list",
|
|
451
|
+
url_params={"ip_addresses": "pk"},
|
|
452
|
+
reverse_lookup="interfaces__ip_addresses",
|
|
453
|
+
distinct=True,
|
|
454
|
+
verbose_name="Devices",
|
|
455
|
+
)
|
|
448
456
|
vm_interface_count = LinkedCountColumn(
|
|
449
457
|
viewname="virtualization:vminterface_list", url_params={"ip_addresses": "pk"}, verbose_name="VM Interfaces"
|
|
450
458
|
)
|
|
451
|
-
vm_interface_parent_count =
|
|
459
|
+
vm_interface_parent_count = LinkedCountColumn(
|
|
460
|
+
viewname="virtualization:virtualmachine_list",
|
|
461
|
+
url_params={"ip_addresses": "pk"},
|
|
462
|
+
reverse_lookup="interfaces__ip_addresses",
|
|
463
|
+
distinct=True,
|
|
464
|
+
verbose_name="Virtual Machines",
|
|
465
|
+
)
|
|
452
466
|
|
|
453
467
|
class Meta(BaseTable.Meta):
|
|
454
468
|
model = IPAddress
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from unittest import skipIf
|
|
3
3
|
|
|
4
|
-
from django.
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.db import connection, transaction
|
|
5
6
|
import netaddr
|
|
6
7
|
|
|
7
8
|
from nautobot.core.testing import TestCase
|
|
8
9
|
from nautobot.extras.models import Status
|
|
9
10
|
from nautobot.ipam import choices
|
|
10
11
|
from nautobot.ipam.models import IPAddress, Namespace, Prefix
|
|
12
|
+
from nautobot.users.models import ObjectPermission
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class IPAddressQuerySet(TestCase):
|
|
@@ -266,6 +268,28 @@ class IPAddressQuerySet(TestCase):
|
|
|
266
268
|
[instance for ip, instance in self.ips.items() if "10.0.0" in ip],
|
|
267
269
|
)
|
|
268
270
|
|
|
271
|
+
def test_lookup_not_ambiguous(self):
|
|
272
|
+
"""Check for issues like https://github.com/nautobot/nautobot/issues/5166."""
|
|
273
|
+
obj_perm = ObjectPermission.objects.create(name="Test Permission", constraints={}, actions=["view"])
|
|
274
|
+
obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
|
|
275
|
+
obj_perm.users.add(self.user)
|
|
276
|
+
queryset = IPAddress.objects.select_related("parent", "nat_inside", "status")
|
|
277
|
+
|
|
278
|
+
for permission_constraint in (
|
|
279
|
+
{"host__family": 4},
|
|
280
|
+
{"host__net_host": "10.0.0.1"},
|
|
281
|
+
{"host__net_host_contained": "10.0.0.0/24"},
|
|
282
|
+
{"host__net_in": ["10.0.0.0/24"]},
|
|
283
|
+
):
|
|
284
|
+
with self.subTest(permission_type=next(iter(permission_constraint.keys()))):
|
|
285
|
+
try:
|
|
286
|
+
with transaction.atomic():
|
|
287
|
+
obj_perm.constraints = permission_constraint
|
|
288
|
+
obj_perm.save()
|
|
289
|
+
list(queryset.restrict(self.user, "view"))
|
|
290
|
+
finally:
|
|
291
|
+
delattr(self.user, "_object_perm_cache")
|
|
292
|
+
|
|
269
293
|
@skipIf(
|
|
270
294
|
connection.vendor == "postgresql",
|
|
271
295
|
"Not currently supported on postgresql",
|
|
@@ -648,6 +672,30 @@ class PrefixQuerysetTestCase(TestCase):
|
|
|
648
672
|
prefix = Prefix.objects.filter(network__net_equals="192.168.0.0/16")[0]
|
|
649
673
|
self.assertEqual(Prefix.objects.filter(prefix="192.168.0.0/16")[0], prefix)
|
|
650
674
|
|
|
675
|
+
def test_lookup_not_ambiguous(self):
|
|
676
|
+
"""Check for issues like https://github.com/nautobot/nautobot/issues/5166."""
|
|
677
|
+
obj_perm = ObjectPermission.objects.create(name="Test Permission", constraints={}, actions=["view"])
|
|
678
|
+
obj_perm.object_types.add(ContentType.objects.get_for_model(Prefix))
|
|
679
|
+
obj_perm.users.add(self.user)
|
|
680
|
+
queryset = Prefix.objects.select_related("parent", "rir", "role", "status", "namespace")
|
|
681
|
+
|
|
682
|
+
for permission_constraint in (
|
|
683
|
+
{"network__family": 4},
|
|
684
|
+
{"network__net_equals": "192.168.0.0/16"},
|
|
685
|
+
{"network__net_contained": "192.0.0.0/8"},
|
|
686
|
+
{"network__net_contained_or_equal": "192.0.0.0/8"},
|
|
687
|
+
{"network__net_contains": "192.168.3.192/32"},
|
|
688
|
+
{"network__net_contains_or_equals": "192.168.3.192/32"},
|
|
689
|
+
):
|
|
690
|
+
with self.subTest(permission_type=next(iter(permission_constraint.keys()))):
|
|
691
|
+
try:
|
|
692
|
+
with transaction.atomic():
|
|
693
|
+
obj_perm.constraints = permission_constraint
|
|
694
|
+
obj_perm.save()
|
|
695
|
+
list(queryset.restrict(self.user, "view"))
|
|
696
|
+
finally:
|
|
697
|
+
delattr(self.user, "_object_perm_cache")
|
|
698
|
+
|
|
651
699
|
@skipIf(
|
|
652
700
|
connection.vendor == "postgresql",
|
|
653
701
|
"Not currently supported on postgresql",
|
nautobot/ipam/utils/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ import netaddr
|
|
|
4
4
|
from nautobot.core.forms.utils import compress_range
|
|
5
5
|
from nautobot.dcim.models import Interface
|
|
6
6
|
from nautobot.extras.models import RelationshipAssociation
|
|
7
|
+
from nautobot.ipam.choices import PrefixTypeChoices
|
|
7
8
|
from nautobot.ipam.models import Prefix, VLAN
|
|
8
9
|
from nautobot.ipam.querysets import IPAddressQuerySet
|
|
9
10
|
from nautobot.virtualization.models import VMInterface
|
|
@@ -25,6 +26,13 @@ def add_available_prefixes(parent, prefix_list):
|
|
|
25
26
|
return prefix_list
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
def get_add_available_prefixes_callback(show_available, parent):
|
|
30
|
+
"""Conditionally provide a callback for add_available_prefixes()."""
|
|
31
|
+
if show_available:
|
|
32
|
+
return lambda prefixes: add_available_prefixes(parent.prefix, prefixes)
|
|
33
|
+
return lambda prefixes: prefixes
|
|
34
|
+
|
|
35
|
+
|
|
28
36
|
def add_available_ipaddresses(prefix, ipaddress_list, is_pool=False):
|
|
29
37
|
"""
|
|
30
38
|
Annotate ranges of available IP addresses within a given prefix. If is_pool is True, the first and last IP will be
|
|
@@ -81,6 +89,15 @@ def add_available_ipaddresses(prefix, ipaddress_list, is_pool=False):
|
|
|
81
89
|
return output
|
|
82
90
|
|
|
83
91
|
|
|
92
|
+
def get_add_available_ipaddresses_callback(show_available, parent):
|
|
93
|
+
"""Conditionally provide a callback for add_available_ipaddresses()."""
|
|
94
|
+
if show_available:
|
|
95
|
+
return lambda ip_addresses: add_available_ipaddresses(
|
|
96
|
+
parent.prefix, ip_addresses, is_pool=(parent.type == PrefixTypeChoices.TYPE_POOL)
|
|
97
|
+
)
|
|
98
|
+
return lambda ip_addresses: ip_addresses
|
|
99
|
+
|
|
100
|
+
|
|
84
101
|
def add_available_vlans(vlan_group, vlans):
|
|
85
102
|
"""
|
|
86
103
|
Create fake records for all gaps between used VLANs
|
|
@@ -99,6 +116,13 @@ def add_available_vlans(vlan_group, vlans):
|
|
|
99
116
|
return vlans
|
|
100
117
|
|
|
101
118
|
|
|
119
|
+
def get_add_available_vlans_callback(show_available, vlan_group):
|
|
120
|
+
"""Conditionally provide a callback for add_available_vlans()."""
|
|
121
|
+
if show_available:
|
|
122
|
+
return lambda vlans: add_available_vlans(vlan_group=vlan_group, vlans=vlans)
|
|
123
|
+
return lambda vlans: vlans
|
|
124
|
+
|
|
125
|
+
|
|
102
126
|
def handle_relationship_changes_when_merging_ips(merged_ip, merged_attributes, collapsed_ips):
|
|
103
127
|
"""
|
|
104
128
|
Update/Delete RelationshipAssociation instances after we collapsed the IPs.
|