nautobot 2.2.4__py3-none-any.whl → 2.2.5__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/forms.py +15 -0
- nautobot/circuits/navigation.py +9 -1
- nautobot/circuits/views.py +2 -0
- nautobot/core/filters.py +11 -0
- nautobot/core/settings.yaml +3 -3
- nautobot/core/testing/filters.py +24 -1
- nautobot/core/testing/views.py +13 -1
- nautobot/core/views/utils.py +18 -1
- nautobot/dcim/filters/__init__.py +1 -1
- nautobot/dcim/forms.py +23 -4
- nautobot/dcim/tables/devicetypes.py +15 -4
- nautobot/dcim/tests/test_views.py +84 -0
- nautobot/dcim/views.py +3 -0
- nautobot/extras/api/views.py +2 -2
- nautobot/extras/context_managers.py +3 -0
- nautobot/extras/filters/__init__.py +15 -1
- nautobot/extras/forms/forms.py +33 -0
- nautobot/extras/forms/mixins.py +0 -6
- nautobot/extras/signals.py +6 -1
- nautobot/extras/tests/test_api.py +24 -2
- nautobot/extras/tests/test_context_managers.py +33 -1
- nautobot/extras/tests/test_filters.py +69 -0
- nautobot/extras/tests/test_forms.py +0 -3
- nautobot/extras/tests/test_views.py +48 -4
- nautobot/extras/views.py +12 -10
- nautobot/ipam/forms.py +18 -0
- nautobot/ipam/tests/test_views.py +9 -2
- nautobot/ipam/views.py +11 -0
- nautobot/project-static/docs/404.html +3 -3
- nautobot/project-static/docs/apps/index.html +3 -3
- nautobot/project-static/docs/apps/nautobot-apps.html +3 -3
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js → bundle.ebd0bdb7.min.js} +5 -5
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js.map → bundle.ebd0bdb7.min.js.map} +3 -3
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css → main.6543a935.min.css} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css.map → main.6543a935.min.css.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +3 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +3 -3
- nautobot/project-static/docs/development/apps/api/configuration-view.html +3 -3
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +3 -3
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +3 -3
- nautobot/project-static/docs/development/apps/api/models/global-search.html +3 -3
- nautobot/project-static/docs/development/apps/api/models/graphql.html +3 -3
- nautobot/project-static/docs/development/apps/api/models/index.html +3 -3
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -4
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +3 -3
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +3 -3
- nautobot/project-static/docs/development/apps/api/prometheus.html +3 -3
- nautobot/project-static/docs/development/apps/api/setup.html +3 -3
- nautobot/project-static/docs/development/apps/api/testing.html +3 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +3 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +3 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +3 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +3 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/base-template.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/index.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/notes.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/urls.html +3 -3
- nautobot/project-static/docs/development/apps/index.html +3 -3
- nautobot/project-static/docs/development/apps/migration/code-updates.html +3 -3
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +3 -3
- nautobot/project-static/docs/development/apps/migration/from-v1.html +5 -5
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +3 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +3 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +3 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +3 -3
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +3 -3
- nautobot/project-static/docs/development/core/application-registry.html +4 -4
- nautobot/project-static/docs/development/core/best-practices.html +3 -3
- nautobot/project-static/docs/development/core/bootstrap-ui.html +3 -3
- nautobot/project-static/docs/development/core/caching.html +3 -3
- nautobot/project-static/docs/development/core/controllers.html +3 -3
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +3 -3
- nautobot/project-static/docs/development/core/generic-views.html +3 -3
- nautobot/project-static/docs/development/core/getting-started.html +5 -5
- nautobot/project-static/docs/development/core/homepage.html +3 -3
- nautobot/project-static/docs/development/core/index.html +3 -3
- nautobot/project-static/docs/development/core/model-checklist.html +3 -3
- nautobot/project-static/docs/development/core/model-features.html +3 -3
- nautobot/project-static/docs/development/core/natural-keys.html +3 -3
- nautobot/project-static/docs/development/core/navigation-menu.html +3 -3
- nautobot/project-static/docs/development/core/release-checklist.html +3 -3
- nautobot/project-static/docs/development/core/role-internals.html +3 -3
- nautobot/project-static/docs/development/core/settings.html +3 -3
- nautobot/project-static/docs/development/core/style-guide.html +3 -3
- nautobot/project-static/docs/development/core/templates.html +3 -3
- nautobot/project-static/docs/development/core/testing.html +4 -4
- nautobot/project-static/docs/development/core/user-preferences.html +3 -3
- nautobot/project-static/docs/development/index.html +3 -3
- nautobot/project-static/docs/development/jobs/index.html +3 -3
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +3 -3
- nautobot/project-static/docs/index.html +3 -3
- nautobot/project-static/docs/release-notes/index.html +3 -3
- nautobot/project-static/docs/release-notes/version-1.0.html +4 -4
- nautobot/project-static/docs/release-notes/version-1.1.html +3 -3
- nautobot/project-static/docs/release-notes/version-1.2.html +5 -5
- nautobot/project-static/docs/release-notes/version-1.3.html +4 -4
- nautobot/project-static/docs/release-notes/version-1.4.html +5 -5
- nautobot/project-static/docs/release-notes/version-1.5.html +5 -5
- nautobot/project-static/docs/release-notes/version-1.6.html +543 -163
- nautobot/project-static/docs/release-notes/version-2.0.html +6 -6
- nautobot/project-static/docs/release-notes/version-2.1.html +3 -3
- nautobot/project-static/docs/release-notes/version-2.2.html +263 -84
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +254 -254
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +3 -3
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +3 -3
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +5 -5
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -4
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +6 -6
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +3 -3
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -4
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +3 -3
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation/index.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +7 -7
- nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -4
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +3 -3
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +3 -3
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +3 -3
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +3 -3
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +3 -3
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +3 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +3 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +3 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +3 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +3 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +3 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +3 -22
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +5 -5
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +5 -5
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +3 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +6 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -4
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +3 -3
- nautobot/project-static/docs/user-guide/index.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +5 -5
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +5 -5
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -36
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +3 -3
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +3 -3
- nautobot/project-static/js/forms.js +2 -1
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/views.py +1 -0
- nautobot/virtualization/forms.py +18 -6
- nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
- nautobot/virtualization/views.py +2 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/METADATA +1 -1
- {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/RECORD +303 -303
- {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/NOTICE +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/WHEEL +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/entry_points.txt +0 -0
|
@@ -17,6 +17,7 @@ from nautobot.core.models.fields import slugify_dashes_to_underscores
|
|
|
17
17
|
from nautobot.core.testing import APITestCase, APIViewTestCases
|
|
18
18
|
from nautobot.core.testing.utils import disable_warnings
|
|
19
19
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
20
|
+
from nautobot.core.utils.permissions import get_permission_for_model
|
|
20
21
|
from nautobot.dcim.models import (
|
|
21
22
|
Controller,
|
|
22
23
|
Device,
|
|
@@ -768,7 +769,7 @@ class DynamicGroupTestMixin:
|
|
|
768
769
|
|
|
769
770
|
# Then the DynamicGroups.
|
|
770
771
|
cls.content_type = ContentType.objects.get_for_model(Device)
|
|
771
|
-
cls.groups =
|
|
772
|
+
cls.groups = [
|
|
772
773
|
DynamicGroup.objects.create(
|
|
773
774
|
name="API DynamicGroup 1",
|
|
774
775
|
content_type=cls.content_type,
|
|
@@ -811,13 +812,34 @@ class DynamicGroupTest(DynamicGroupTestMixin, APIViewTestCases.APIViewTestCase):
|
|
|
811
812
|
def test_get_members(self):
|
|
812
813
|
"""Test that the `/members/` API endpoint returns what is expected."""
|
|
813
814
|
self.add_permissions("extras.view_dynamicgroup")
|
|
814
|
-
instance =
|
|
815
|
+
instance = self.groups[0]
|
|
816
|
+
self.add_permissions(get_permission_for_model(instance.content_type.model_class(), "view"))
|
|
815
817
|
member_count = instance.members.count()
|
|
816
818
|
url = reverse("extras-api:dynamicgroup-members", kwargs={"pk": instance.pk})
|
|
817
819
|
response = self.client.get(url, **self.header)
|
|
818
820
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
819
821
|
self.assertEqual(member_count, len(response.json()["results"]))
|
|
820
822
|
|
|
823
|
+
def test_get_members_with_constrained_permission(self):
|
|
824
|
+
"""Test that the `/members/` API endpoint enforces permissions on the member model."""
|
|
825
|
+
self.add_permissions("extras.view_dynamicgroup")
|
|
826
|
+
instance = self.groups[0]
|
|
827
|
+
obj1 = instance.members.first()
|
|
828
|
+
obj_perm = ObjectPermission(
|
|
829
|
+
name="Test permission",
|
|
830
|
+
constraints={"pk__in": [obj1.pk]},
|
|
831
|
+
actions=["view"],
|
|
832
|
+
)
|
|
833
|
+
obj_perm.save()
|
|
834
|
+
obj_perm.users.add(self.user)
|
|
835
|
+
obj_perm.object_types.add(instance.content_type)
|
|
836
|
+
|
|
837
|
+
url = reverse("extras-api:dynamicgroup-members", kwargs={"pk": instance.pk})
|
|
838
|
+
response = self.client.get(url, **self.header)
|
|
839
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
840
|
+
self.assertEqual(len(response.json()["results"]), 1)
|
|
841
|
+
self.assertEqual(response.json()["results"][0]["id"], str(obj1.pk))
|
|
842
|
+
|
|
821
843
|
|
|
822
844
|
class DynamicGroupMembershipTest(DynamicGroupTestMixin, APIViewTestCases.APIViewTestCase):
|
|
823
845
|
model = DynamicGroupMembership
|
|
@@ -5,7 +5,16 @@ from django.test import TestCase
|
|
|
5
5
|
from nautobot.core.celery import app
|
|
6
6
|
from nautobot.core.testing import TransactionTestCase
|
|
7
7
|
from nautobot.core.utils.lookup import get_changes_for_model
|
|
8
|
-
from nautobot.dcim.models import
|
|
8
|
+
from nautobot.dcim.models import (
|
|
9
|
+
DeviceType,
|
|
10
|
+
DeviceTypeToSoftwareImageFile,
|
|
11
|
+
Location,
|
|
12
|
+
LocationType,
|
|
13
|
+
Manufacturer,
|
|
14
|
+
Platform,
|
|
15
|
+
SoftwareImageFile,
|
|
16
|
+
SoftwareVersion,
|
|
17
|
+
)
|
|
9
18
|
from nautobot.extras.choices import ObjectChangeActionChoices, ObjectChangeEventContextChoices
|
|
10
19
|
from nautobot.extras.context_managers import (
|
|
11
20
|
deferred_change_logging_for_bulk_operation,
|
|
@@ -294,6 +303,29 @@ class BulkEditDeleteChangeLogging(TestCase):
|
|
|
294
303
|
self.assertIsNone(snapshots["differences"]["removed"])
|
|
295
304
|
self.assertEqual(snapshots["differences"]["added"]["description"], "changed")
|
|
296
305
|
|
|
306
|
+
def test_bulk_edit_device_type_software_image_file(self):
|
|
307
|
+
"""Test that bulk edits to null does not cause integrity error"""
|
|
308
|
+
manufacturer = Manufacturer.objects.create(name="Test")
|
|
309
|
+
platform = Platform.objects.create(name="Test")
|
|
310
|
+
software_status = Status.objects.get_for_model(SoftwareVersion).first()
|
|
311
|
+
software_version = SoftwareVersion.objects.create(version="1.0.0", platform=platform, status=software_status)
|
|
312
|
+
software_image_file = SoftwareImageFile.objects.create(
|
|
313
|
+
image_file_name="test.iso", software_version=software_version, status=software_status
|
|
314
|
+
)
|
|
315
|
+
device_type = DeviceType.objects.create(manufacturer=manufacturer, model="test123")
|
|
316
|
+
device_type.software_image_files.set([software_image_file])
|
|
317
|
+
with web_request_context(self.user):
|
|
318
|
+
with deferred_change_logging_for_bulk_operation():
|
|
319
|
+
device_type.software_image_files.set([])
|
|
320
|
+
device_type.save()
|
|
321
|
+
|
|
322
|
+
oc_list = get_changes_for_model(DeviceTypeToSoftwareImageFile)
|
|
323
|
+
self.assertEqual(len(oc_list), 1)
|
|
324
|
+
self.assertEqual(oc_list[0].action, ObjectChangeActionChoices.ACTION_DELETE)
|
|
325
|
+
self.assertIsNotNone(oc_list[0].changed_object_id)
|
|
326
|
+
self.assertEqual(oc_list[0].user, self.user)
|
|
327
|
+
self.assertEqual(oc_list[0].user_name, self.user.username)
|
|
328
|
+
|
|
297
329
|
def test_change_log_context(self):
|
|
298
330
|
location_type = LocationType.objects.get(name="Campus")
|
|
299
331
|
location_status = Status.objects.get_for_model(Location).first()
|
|
@@ -30,6 +30,7 @@ from nautobot.extras.constants import HTTP_CONTENT_TYPE_JSON
|
|
|
30
30
|
from nautobot.extras.filters import (
|
|
31
31
|
ComputedFieldFilterSet,
|
|
32
32
|
ConfigContextFilterSet,
|
|
33
|
+
ContactAssociationFilterSet,
|
|
33
34
|
ContactFilterSet,
|
|
34
35
|
ContentTypeFilterSet,
|
|
35
36
|
CustomFieldChoiceFilterSet,
|
|
@@ -555,6 +556,74 @@ class ContactFilterSetTestCase(ContactAndTeamFilterSetTestCaseMixin, FilterTestC
|
|
|
555
556
|
)
|
|
556
557
|
|
|
557
558
|
|
|
559
|
+
class ContactAssociationFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
560
|
+
queryset = ContactAssociation.objects.all()
|
|
561
|
+
filterset = ContactAssociationFilterSet
|
|
562
|
+
|
|
563
|
+
generic_filter_tests = (
|
|
564
|
+
["status", "status__id"],
|
|
565
|
+
["status", "status__name"],
|
|
566
|
+
["contact", "contact__id"],
|
|
567
|
+
["contact", "contact__name"],
|
|
568
|
+
["team", "team__id"],
|
|
569
|
+
["team", "team__name"],
|
|
570
|
+
["role", "role__id"],
|
|
571
|
+
["role", "role__name"],
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
@classmethod
|
|
575
|
+
def setUpTestData(cls):
|
|
576
|
+
roles = Role.objects.get_for_model(ContactAssociation)
|
|
577
|
+
statuses = Status.objects.get_for_model(ContactAssociation)
|
|
578
|
+
ip_addresses = IPAddress.objects.all()
|
|
579
|
+
locations = Location.objects.all()
|
|
580
|
+
|
|
581
|
+
cls.location_ct = ContentType.objects.get_for_model(Location)
|
|
582
|
+
ipaddress_ct = ContentType.objects.get_for_model(IPAddress)
|
|
583
|
+
|
|
584
|
+
ContactAssociation.objects.create(
|
|
585
|
+
contact=Contact.objects.first(),
|
|
586
|
+
associated_object_type=ipaddress_ct,
|
|
587
|
+
associated_object_id=ip_addresses[0].pk,
|
|
588
|
+
role=roles[2],
|
|
589
|
+
status=statuses[1],
|
|
590
|
+
)
|
|
591
|
+
ContactAssociation.objects.create(
|
|
592
|
+
contact=Contact.objects.last(),
|
|
593
|
+
associated_object_type=ipaddress_ct,
|
|
594
|
+
associated_object_id=ip_addresses[1].pk,
|
|
595
|
+
role=roles[1],
|
|
596
|
+
status=statuses[2],
|
|
597
|
+
)
|
|
598
|
+
ContactAssociation.objects.create(
|
|
599
|
+
team=Team.objects.first(),
|
|
600
|
+
associated_object_type=cls.location_ct,
|
|
601
|
+
associated_object_id=locations[0].pk,
|
|
602
|
+
role=roles[3],
|
|
603
|
+
status=statuses[0],
|
|
604
|
+
)
|
|
605
|
+
ContactAssociation.objects.create(
|
|
606
|
+
team=Team.objects.last(),
|
|
607
|
+
associated_object_type=cls.location_ct,
|
|
608
|
+
associated_object_id=locations[1].pk,
|
|
609
|
+
role=roles[0],
|
|
610
|
+
status=statuses[1],
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
def test_associated_object_type(self):
|
|
614
|
+
params = {"associated_object_type": "dcim.location"}
|
|
615
|
+
self.assertEqual(
|
|
616
|
+
self.filterset(params, self.queryset).qs.count(),
|
|
617
|
+
ContactAssociation.objects.filter(associated_object_type=self.location_ct).count(),
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
params = {"associated_object_type": self.location_ct.pk}
|
|
621
|
+
self.assertEqual(
|
|
622
|
+
self.filterset(params, self.queryset).qs.count(),
|
|
623
|
+
ContactAssociation.objects.filter(associated_object_type=self.location_ct).count(),
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
|
|
558
627
|
class CustomFieldChoiceFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
559
628
|
queryset = CustomFieldChoice.objects.all()
|
|
560
629
|
filterset = CustomFieldChoiceFilterSet
|
|
@@ -14,7 +14,6 @@ from nautobot.extras.forms import (
|
|
|
14
14
|
ConfigContextFilterForm,
|
|
15
15
|
ConfigContextForm,
|
|
16
16
|
CustomFieldModelBulkEditFormMixin,
|
|
17
|
-
CustomFieldModelFilterFormMixin,
|
|
18
17
|
CustomFieldModelFormMixin,
|
|
19
18
|
JobButtonForm,
|
|
20
19
|
JobEditForm,
|
|
@@ -1089,7 +1088,6 @@ class DeprecatedAliasesTestCase(TestCase):
|
|
|
1089
1088
|
AddRemoveTagsForm,
|
|
1090
1089
|
CustomFieldBulkCreateForm,
|
|
1091
1090
|
CustomFieldBulkEditForm,
|
|
1092
|
-
CustomFieldFilterForm,
|
|
1093
1091
|
CustomFieldModelForm,
|
|
1094
1092
|
RelationshipModelForm,
|
|
1095
1093
|
StatusBulkEditFormMixin,
|
|
@@ -1100,7 +1098,6 @@ class DeprecatedAliasesTestCase(TestCase):
|
|
|
1100
1098
|
(AddRemoveTagsForm, TagsBulkEditFormMixin),
|
|
1101
1099
|
(CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
|
|
1102
1100
|
(CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
|
|
1103
|
-
(CustomFieldFilterForm, CustomFieldModelFilterFormMixin),
|
|
1104
1101
|
(CustomFieldModelForm, CustomFieldModelFormMixin),
|
|
1105
1102
|
(RelationshipModelForm, RelationshipModelFormMixin),
|
|
1106
1103
|
(StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
|
|
@@ -17,6 +17,7 @@ from nautobot.core.choices import ColorChoices
|
|
|
17
17
|
from nautobot.core.models.fields import slugify_dashes_to_underscores
|
|
18
18
|
from nautobot.core.testing import extract_form_failures, extract_page_body, TestCase, ViewTestCases
|
|
19
19
|
from nautobot.core.testing.utils import disable_warnings, post_data
|
|
20
|
+
from nautobot.core.utils.permissions import get_permission_for_model
|
|
20
21
|
from nautobot.dcim.models import (
|
|
21
22
|
ConsolePort,
|
|
22
23
|
Controller,
|
|
@@ -777,9 +778,11 @@ class DynamicGroupTestCase(
|
|
|
777
778
|
content_type = ContentType.objects.get_for_model(Device)
|
|
778
779
|
|
|
779
780
|
# DynamicGroup objects to test.
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
781
|
+
cls.dynamic_groups = [
|
|
782
|
+
DynamicGroup.objects.create(name="DG 1", content_type=content_type),
|
|
783
|
+
DynamicGroup.objects.create(name="DG 2", content_type=content_type),
|
|
784
|
+
DynamicGroup.objects.create(name="DG 3", content_type=content_type),
|
|
785
|
+
]
|
|
783
786
|
|
|
784
787
|
cls.form_data = {
|
|
785
788
|
"name": "new_dynamic_group",
|
|
@@ -792,6 +795,38 @@ class DynamicGroupTestCase(
|
|
|
792
795
|
"dynamic_group_memberships-MAX_NUM_FORMS": "1000",
|
|
793
796
|
}
|
|
794
797
|
|
|
798
|
+
def test_get_object_with_permission(self):
|
|
799
|
+
instance = self._get_queryset().first()
|
|
800
|
+
# Add view permissions for the group's members:
|
|
801
|
+
self.add_permissions(get_permission_for_model(instance.content_type.model_class(), "view"))
|
|
802
|
+
|
|
803
|
+
response = super().test_get_object_with_permission()
|
|
804
|
+
|
|
805
|
+
response_body = extract_page_body(response.content.decode(response.charset))
|
|
806
|
+
# Check that the "members" table in the detail view includes all appropriate member objects
|
|
807
|
+
for member in instance.members:
|
|
808
|
+
self.assertIn(str(member.pk), response_body)
|
|
809
|
+
|
|
810
|
+
def test_get_object_with_constrained_permission(self):
|
|
811
|
+
instance = self._get_queryset().first()
|
|
812
|
+
# Add view permission for one of the group's members but not the others:
|
|
813
|
+
member1, member2 = instance.members[:2]
|
|
814
|
+
obj_perm = ObjectPermission(
|
|
815
|
+
name="Members permission",
|
|
816
|
+
constraints={"pk": member1.pk},
|
|
817
|
+
actions=["view"],
|
|
818
|
+
)
|
|
819
|
+
obj_perm.save()
|
|
820
|
+
obj_perm.users.add(self.user)
|
|
821
|
+
obj_perm.object_types.add(instance.content_type)
|
|
822
|
+
|
|
823
|
+
response = super().test_get_object_with_constrained_permission()
|
|
824
|
+
|
|
825
|
+
response_body = extract_page_body(response.content.decode(response.charset))
|
|
826
|
+
# Check that the "members" table in the detail view includes all permitted member objects
|
|
827
|
+
self.assertIn(str(member1.pk), response_body)
|
|
828
|
+
self.assertNotIn(str(member2.pk), response_body)
|
|
829
|
+
|
|
795
830
|
def test_get_object_dynamic_groups_anonymous(self):
|
|
796
831
|
url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.first().pk})
|
|
797
832
|
self.client.logout()
|
|
@@ -815,7 +850,6 @@ class DynamicGroupTestCase(
|
|
|
815
850
|
self.assertIn("DG 3", response_body, msg=response_body)
|
|
816
851
|
|
|
817
852
|
def test_get_object_dynamic_groups_with_constrained_permission(self):
|
|
818
|
-
self.add_permissions("extras.view_dynamicgroup")
|
|
819
853
|
obj_perm = ObjectPermission(
|
|
820
854
|
name="View a device",
|
|
821
855
|
constraints={"pk": Device.objects.first().pk},
|
|
@@ -824,12 +858,22 @@ class DynamicGroupTestCase(
|
|
|
824
858
|
obj_perm.save()
|
|
825
859
|
obj_perm.users.add(self.user)
|
|
826
860
|
obj_perm.object_types.add(ContentType.objects.get_for_model(Device))
|
|
861
|
+
obj_perm_2 = ObjectPermission(
|
|
862
|
+
name="View a Dynamic Group",
|
|
863
|
+
constraints={"pk": self.dynamic_groups[0].pk},
|
|
864
|
+
actions=["view"],
|
|
865
|
+
)
|
|
866
|
+
obj_perm_2.save()
|
|
867
|
+
obj_perm_2.users.add(self.user)
|
|
868
|
+
obj_perm_2.object_types.add(ContentType.objects.get_for_model(DynamicGroup))
|
|
827
869
|
|
|
828
870
|
url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.first().pk})
|
|
829
871
|
response = self.client.get(url)
|
|
830
872
|
self.assertHttpStatus(response, 200)
|
|
831
873
|
response_body = response.content.decode(response.charset)
|
|
832
874
|
self.assertIn("DG 1", response_body, msg=response_body)
|
|
875
|
+
self.assertNotIn("DG 2", response_body, msg=response_body)
|
|
876
|
+
self.assertNotIn("DG 3", response_body, msg=response_body)
|
|
833
877
|
|
|
834
878
|
url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.last().pk})
|
|
835
879
|
response = self.client.get(url)
|
nautobot/extras/views.py
CHANGED
|
@@ -56,8 +56,6 @@ from .datasources import (
|
|
|
56
56
|
enqueue_pull_git_repository_and_refresh_data,
|
|
57
57
|
get_datasource_contents,
|
|
58
58
|
)
|
|
59
|
-
from .filters import RoleFilterSet
|
|
60
|
-
from .forms import RoleBulkEditForm, RoleForm
|
|
61
59
|
from .jobs import get_job
|
|
62
60
|
from .models import (
|
|
63
61
|
ComputedField,
|
|
@@ -94,7 +92,6 @@ from .models import (
|
|
|
94
92
|
Webhook,
|
|
95
93
|
)
|
|
96
94
|
from .registry import registry
|
|
97
|
-
from .tables import AssociatedContactsTable, RoleTable
|
|
98
95
|
|
|
99
96
|
logger = logging.getLogger(__name__)
|
|
100
97
|
|
|
@@ -398,7 +395,7 @@ class ContactAssociationUIViewSet(
|
|
|
398
395
|
filterset_class = filters.ContactAssociationFilterSet
|
|
399
396
|
queryset = ContactAssociation.objects.all()
|
|
400
397
|
serializer_class = serializers.ContactAssociationSerializer
|
|
401
|
-
table_class = AssociatedContactsTable
|
|
398
|
+
table_class = tables.AssociatedContactsTable
|
|
402
399
|
non_filter_params = ("export", "page", "per_page", "sort")
|
|
403
400
|
|
|
404
401
|
|
|
@@ -506,6 +503,7 @@ class CustomFieldListView(generic.ObjectListView):
|
|
|
506
503
|
queryset = CustomField.objects.all()
|
|
507
504
|
table = tables.CustomFieldTable
|
|
508
505
|
filterset = filters.CustomFieldFilterSet
|
|
506
|
+
filterset_form = forms.CustomFieldFilterForm
|
|
509
507
|
action_buttons = ("add",)
|
|
510
508
|
|
|
511
509
|
|
|
@@ -706,7 +704,7 @@ class DynamicGroupView(generic.ObjectView):
|
|
|
706
704
|
|
|
707
705
|
if table_class is not None:
|
|
708
706
|
# Members table (for display on Members nav tab)
|
|
709
|
-
members_table = table_class(instance.members, orderable=False)
|
|
707
|
+
members_table = table_class(instance.members.restrict(request.user, "view"), orderable=False)
|
|
710
708
|
paginate = {
|
|
711
709
|
"paginator_class": EnhancedPaginator,
|
|
712
710
|
"per_page": get_paginate_count(request),
|
|
@@ -886,7 +884,9 @@ class ObjectDynamicGroupsView(generic.GenericView):
|
|
|
886
884
|
obj = get_object_or_404(model, **kwargs)
|
|
887
885
|
|
|
888
886
|
# Gather all dynamic groups for this object (and its related objects)
|
|
889
|
-
dynamicsgroups_table = tables.DynamicGroupTable(
|
|
887
|
+
dynamicsgroups_table = tables.DynamicGroupTable(
|
|
888
|
+
data=obj.dynamic_groups_cached.restrict(request.user, "view"), orderable=False
|
|
889
|
+
)
|
|
890
890
|
|
|
891
891
|
# Apply the request context
|
|
892
892
|
paginate = {
|
|
@@ -950,6 +950,7 @@ class ExportTemplateBulkDeleteView(generic.BulkDeleteView):
|
|
|
950
950
|
class ExternalIntegrationUIViewSet(NautobotUIViewSet):
|
|
951
951
|
bulk_update_form_class = forms.ExternalIntegrationBulkEditForm
|
|
952
952
|
filterset_class = filters.ExternalIntegrationFilterSet
|
|
953
|
+
filterset_form_class = forms.ExternalIntegrationFilterForm
|
|
953
954
|
form_class = forms.ExternalIntegrationForm
|
|
954
955
|
queryset = ExternalIntegration.objects.select_related("secrets_group")
|
|
955
956
|
serializer_class = serializers.ExternalIntegrationSerializer
|
|
@@ -2048,11 +2049,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
|
|
|
2048
2049
|
"""`Roles` UIViewSet."""
|
|
2049
2050
|
|
|
2050
2051
|
queryset = Role.objects.all()
|
|
2051
|
-
bulk_update_form_class = RoleBulkEditForm
|
|
2052
|
-
filterset_class = RoleFilterSet
|
|
2053
|
-
|
|
2052
|
+
bulk_update_form_class = forms.RoleBulkEditForm
|
|
2053
|
+
filterset_class = filters.RoleFilterSet
|
|
2054
|
+
filterset_form_class = forms.RoleFilterForm
|
|
2055
|
+
form_class = forms.RoleForm
|
|
2054
2056
|
serializer_class = serializers.RoleSerializer
|
|
2055
|
-
table_class = RoleTable
|
|
2057
|
+
table_class = tables.RoleTable
|
|
2056
2058
|
|
|
2057
2059
|
def get_extra_context(self, request, instance):
|
|
2058
2060
|
context = super().get_extra_context(request, instance)
|
nautobot/ipam/forms.py
CHANGED
|
@@ -94,6 +94,12 @@ class NamespaceBulkEditForm(
|
|
|
94
94
|
]
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
class NamespaceFilterForm(LocatableModelFilterFormMixin, NautobotFilterForm):
|
|
98
|
+
model = Namespace
|
|
99
|
+
q = forms.CharField(required=False, label="Search")
|
|
100
|
+
name = forms.CharField(required=False)
|
|
101
|
+
|
|
102
|
+
|
|
97
103
|
#
|
|
98
104
|
# VRFs
|
|
99
105
|
#
|
|
@@ -142,6 +148,12 @@ class VRFBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
|
142
148
|
namespace = DynamicModelChoiceField(queryset=Namespace.objects.all(), required=False)
|
|
143
149
|
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
144
150
|
description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
|
|
151
|
+
add_prefixes = DynamicModelMultipleChoiceField(
|
|
152
|
+
queryset=Prefix.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
153
|
+
)
|
|
154
|
+
remove_prefixes = DynamicModelMultipleChoiceField(
|
|
155
|
+
queryset=Prefix.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
156
|
+
)
|
|
145
157
|
|
|
146
158
|
class Meta:
|
|
147
159
|
nullable_fields = [
|
|
@@ -358,6 +370,12 @@ class PrefixBulkEditForm(
|
|
|
358
370
|
remove_locations = DynamicModelMultipleChoiceField(
|
|
359
371
|
queryset=Location.objects.all(), required=False, query_params={"content_type": Prefix._meta.label_lower}
|
|
360
372
|
)
|
|
373
|
+
add_vrfs = DynamicModelMultipleChoiceField(
|
|
374
|
+
queryset=VRF.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
375
|
+
)
|
|
376
|
+
remove_vrfs = DynamicModelMultipleChoiceField(
|
|
377
|
+
queryset=VRF.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
378
|
+
)
|
|
361
379
|
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
362
380
|
rir = DynamicModelChoiceField(queryset=RIR.objects.all(), required=False, label="RIR")
|
|
363
381
|
date_allocated = forms.DateTimeField(required=False, widget=DateTimePicker)
|
|
@@ -71,7 +71,8 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
71
71
|
@classmethod
|
|
72
72
|
def setUpTestData(cls):
|
|
73
73
|
tenants = Tenant.objects.all()[:2]
|
|
74
|
-
namespace =
|
|
74
|
+
namespace = Prefix.objects.first().namespace
|
|
75
|
+
prefixes = Prefix.objects.filter(namespace=namespace)
|
|
75
76
|
|
|
76
77
|
cls.form_data = {
|
|
77
78
|
"name": "VRF X",
|
|
@@ -79,12 +80,16 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
79
80
|
"rd": "65000:999",
|
|
80
81
|
"tenant": tenants[0].pk,
|
|
81
82
|
"description": "A new VRF",
|
|
83
|
+
"prefixes": [prefixes[1].id],
|
|
82
84
|
"tags": [t.pk for t in Tag.objects.get_for_model(VRF)],
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
cls.bulk_edit_data = {
|
|
86
88
|
"tenant": tenants[1].pk,
|
|
87
89
|
"description": "New description",
|
|
90
|
+
"namespace": prefixes[0].namespace.id,
|
|
91
|
+
"add_prefixes": [prefixes[0].id],
|
|
92
|
+
"remove_prefixes": [prefixes[1].id],
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
|
|
@@ -158,7 +163,6 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
|
|
|
158
163
|
|
|
159
164
|
cls.bulk_edit_data = {
|
|
160
165
|
"tenant": None,
|
|
161
|
-
# TODO "vrf": vrfs[1].pk,
|
|
162
166
|
"status": cls.statuses[1].pk,
|
|
163
167
|
"role": cls.roles[1].pk,
|
|
164
168
|
"rir": RIR.objects.last().pk,
|
|
@@ -166,6 +170,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
|
|
|
166
170
|
"description": "New description",
|
|
167
171
|
"add_locations": [cls.locations[0].pk],
|
|
168
172
|
"remove_locations": [cls.locations[1].pk],
|
|
173
|
+
"namespace": vrfs[0].namespace.pk,
|
|
174
|
+
"add_vrfs": [vrfs[0].pk],
|
|
175
|
+
"remove_vrfs": [vrfs[1].pk],
|
|
169
176
|
}
|
|
170
177
|
|
|
171
178
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
nautobot/ipam/views.py
CHANGED
|
@@ -80,6 +80,7 @@ class NamespaceUIViewSet(
|
|
|
80
80
|
form_class = forms.NamespaceForm
|
|
81
81
|
bulk_update_form_class = forms.NamespaceBulkEditForm
|
|
82
82
|
filterset_class = filters.NamespaceFilterSet
|
|
83
|
+
filterset_form_class = forms.NamespaceFilterForm
|
|
83
84
|
queryset = Namespace.objects.all()
|
|
84
85
|
serializer_class = serializers.NamespaceSerializer
|
|
85
86
|
table_class = tables.NamespaceTable
|
|
@@ -303,6 +304,12 @@ class VRFBulkEditView(generic.BulkEditView):
|
|
|
303
304
|
table = tables.VRFTable
|
|
304
305
|
form = forms.VRFBulkEditForm
|
|
305
306
|
|
|
307
|
+
def extra_post_save_action(self, obj, form):
|
|
308
|
+
if form.cleaned_data.get("add_prefixes", None):
|
|
309
|
+
obj.prefixes.add(*form.cleaned_data["add_prefixes"])
|
|
310
|
+
if form.cleaned_data.get("remove_prefixes", None):
|
|
311
|
+
obj.prefixes.remove(*form.cleaned_data["remove_prefixes"])
|
|
312
|
+
|
|
306
313
|
|
|
307
314
|
class VRFBulkDeleteView(generic.BulkDeleteView):
|
|
308
315
|
queryset = VRF.objects.select_related("tenant")
|
|
@@ -713,6 +720,10 @@ class PrefixBulkEditView(generic.BulkEditView):
|
|
|
713
720
|
obj.locations.add(*form.cleaned_data["add_locations"])
|
|
714
721
|
if form.cleaned_data.get("remove_locations", None):
|
|
715
722
|
obj.locations.remove(*form.cleaned_data["remove_locations"])
|
|
723
|
+
if form.cleaned_data.get("add_vrfs", None):
|
|
724
|
+
obj.vrfs.add(*form.cleaned_data["add_vrfs"])
|
|
725
|
+
if form.cleaned_data.get("remove_vrfs", None):
|
|
726
|
+
obj.vrfs.remove(*form.cleaned_data["remove_vrfs"])
|
|
716
727
|
|
|
717
728
|
|
|
718
729
|
class PrefixBulkDeleteView(generic.BulkDeleteView):
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
<link rel="icon" href="/projects/core/en/stable/assets/favicon.ico">
|
|
15
|
-
<meta name="generator" content="mkdocs-1.
|
|
15
|
+
<meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
<link rel="stylesheet" href="/projects/core/en/stable/assets/stylesheets/main.
|
|
23
|
+
<link rel="stylesheet" href="/projects/core/en/stable/assets/stylesheets/main.6543a935.min.css">
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
<link rel="stylesheet" href="/projects/core/en/stable/assets/stylesheets/palette.06af60db.min.css">
|
|
@@ -7838,7 +7838,7 @@
|
|
|
7838
7838
|
<script id="__config" type="application/json">{"base": "/projects/core/en/stable/", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "/projects/core/en/stable/assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
|
|
7839
7839
|
|
|
7840
7840
|
|
|
7841
|
-
<script src="/projects/core/en/stable/assets/javascripts/bundle.
|
|
7841
|
+
<script src="/projects/core/en/stable/assets/javascripts/bundle.ebd0bdb7.min.js"></script>
|
|
7842
7842
|
|
|
7843
7843
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
7844
7844
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
<link rel="icon" href="../assets/favicon.ico">
|
|
21
|
-
<meta name="generator" content="mkdocs-1.
|
|
21
|
+
<meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
<link rel="stylesheet" href="../assets/stylesheets/main.
|
|
29
|
+
<link rel="stylesheet" href="../assets/stylesheets/main.6543a935.min.css">
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
|
|
@@ -7996,7 +7996,7 @@
|
|
|
7996
7996
|
<script id="__config" type="application/json">{"base": "..", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
|
|
7997
7997
|
|
|
7998
7998
|
|
|
7999
|
-
<script src="../assets/javascripts/bundle.
|
|
7999
|
+
<script src="../assets/javascripts/bundle.ebd0bdb7.min.js"></script>
|
|
8000
8000
|
|
|
8001
8001
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
8002
8002
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
<link rel="icon" href="../assets/favicon.ico">
|
|
19
|
-
<meta name="generator" content="mkdocs-1.
|
|
19
|
+
<meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
<link rel="stylesheet" href="../assets/stylesheets/main.
|
|
27
|
+
<link rel="stylesheet" href="../assets/stylesheets/main.6543a935.min.css">
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
|
|
@@ -7930,7 +7930,7 @@
|
|
|
7930
7930
|
<script id="__config" type="application/json">{"base": "..", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
|
|
7931
7931
|
|
|
7932
7932
|
|
|
7933
|
-
<script src="../assets/javascripts/bundle.
|
|
7933
|
+
<script src="../assets/javascripts/bundle.ebd0bdb7.min.js"></script>
|
|
7934
7934
|
|
|
7935
7935
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
7936
7936
|
|