nautobot 2.2.3__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.py +6 -4
- nautobot/core/settings.yaml +54 -19
- nautobot/core/templates/admin/base.html +2 -2
- nautobot/core/templates/base_django.html +2 -2
- nautobot/core/templates/buttons/export.html +47 -47
- nautobot/core/templates/inc/javascript.html +3 -0
- nautobot/core/templates/inc/media.html +3 -0
- nautobot/core/templates/login.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +2 -0
- nautobot/core/testing/filters.py +24 -1
- nautobot/core/testing/views.py +13 -1
- nautobot/core/tests/test_jobs.py +79 -2
- nautobot/core/tests/test_views.py +33 -0
- nautobot/core/views/mixins.py +4 -0
- 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 +323 -55
- nautobot/dcim/views.py +26 -20
- nautobot/extras/api/serializers.py +17 -6
- 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 +51 -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/utils.py +2 -1
- nautobot/extras/views.py +47 -31
- nautobot/ipam/forms.py +18 -0
- nautobot/ipam/tests/test_views.py +9 -2
- nautobot/ipam/views.py +17 -6
- nautobot/project-static/docs/404.html +107 -51
- nautobot/project-static/docs/apps/index.html +107 -51
- nautobot/project-static/docs/apps/nautobot-apps.html +107 -51
- nautobot/project-static/docs/assets/_mkdocstrings.css +6 -1
- nautobot/project-static/docs/assets/extra.css +7 -0
- nautobot/project-static/docs/assets/javascripts/bundle.ebd0bdb7.min.js +29 -0
- nautobot/project-static/docs/assets/javascripts/bundle.ebd0bdb7.min.js.map +7 -0
- nautobot/project-static/docs/assets/stylesheets/main.6543a935.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.6543a935.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +107 -51
- nautobot/project-static/docs/development/apps/api/configuration-view.html +110 -54
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +110 -54
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +107 -51
- nautobot/project-static/docs/development/apps/api/models/global-search.html +110 -54
- nautobot/project-static/docs/development/apps/api/models/graphql.html +113 -57
- nautobot/project-static/docs/development/apps/api/models/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +113 -57
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +107 -51
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +111 -55
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +107 -51
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +107 -51
- nautobot/project-static/docs/development/apps/api/prometheus.html +110 -54
- nautobot/project-static/docs/development/apps/api/setup.html +107 -51
- nautobot/project-static/docs/development/apps/api/testing.html +113 -57
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +110 -54
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +110 -54
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +107 -51
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +113 -57
- nautobot/project-static/docs/development/apps/api/views/base-template.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +113 -57
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +122 -66
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/notes.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/urls.html +107 -51
- nautobot/project-static/docs/development/apps/index.html +128 -72
- nautobot/project-static/docs/development/apps/migration/code-updates.html +107 -51
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +107 -51
- nautobot/project-static/docs/development/apps/migration/from-v1.html +109 -53
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +107 -51
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +110 -54
- nautobot/project-static/docs/development/core/application-registry.html +120 -64
- nautobot/project-static/docs/development/core/best-practices.html +122 -66
- nautobot/project-static/docs/development/core/bootstrap-ui.html +107 -51
- nautobot/project-static/docs/development/core/caching.html +107 -51
- nautobot/project-static/docs/development/core/controllers.html +107 -51
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +113 -57
- nautobot/project-static/docs/development/core/generic-views.html +110 -54
- nautobot/project-static/docs/development/core/getting-started.html +137 -81
- nautobot/project-static/docs/development/core/homepage.html +110 -54
- nautobot/project-static/docs/development/core/index.html +107 -51
- nautobot/project-static/docs/development/core/model-checklist.html +107 -51
- nautobot/project-static/docs/development/core/model-features.html +107 -51
- nautobot/project-static/docs/development/core/natural-keys.html +110 -54
- nautobot/project-static/docs/development/core/navigation-menu.html +107 -51
- nautobot/project-static/docs/development/core/release-checklist.html +107 -51
- nautobot/project-static/docs/development/core/role-internals.html +107 -51
- nautobot/project-static/docs/development/core/settings.html +107 -51
- nautobot/project-static/docs/development/core/style-guide.html +110 -54
- nautobot/project-static/docs/development/core/templates.html +113 -57
- nautobot/project-static/docs/development/core/testing.html +126 -70
- nautobot/project-static/docs/development/core/user-preferences.html +107 -51
- nautobot/project-static/docs/development/index.html +107 -51
- nautobot/project-static/docs/development/jobs/index.html +173 -117
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +110 -54
- nautobot/project-static/docs/docker/index.html +3 -3
- nautobot/project-static/docs/index.html +125 -69
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +3 -3
- nautobot/project-static/docs/release-notes/index.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.0.html +108 -52
- nautobot/project-static/docs/release-notes/version-1.1.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.2.html +109 -53
- nautobot/project-static/docs/release-notes/version-1.3.html +108 -52
- nautobot/project-static/docs/release-notes/version-1.4.html +109 -53
- nautobot/project-static/docs/release-notes/version-1.5.html +118 -62
- nautobot/project-static/docs/release-notes/version-1.6.html +721 -285
- nautobot/project-static/docs/release-notes/version-2.0.html +113 -57
- nautobot/project-static/docs/release-notes/version-2.1.html +107 -51
- nautobot/project-static/docs/release-notes/version-2.2.html +503 -120
- nautobot/project-static/docs/requirements.txt +4 -4
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +262 -262
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +107 -51
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +107 -51
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +109 -53
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +108 -52
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +254 -167
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +113 -57
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +113 -57
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +113 -57
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +108 -52
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +107 -51
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +171 -112
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +13 -8626
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +117 -61
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +13 -8614
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +252 -165
- nautobot/project-static/docs/user-guide/administration/installation/index.html +165 -192
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +411 -691
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +249 -230
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +13 -8118
- nautobot/project-static/docs/user-guide/administration/installation/services.html +351 -241
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +8684 -0
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +8672 -0
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +8176 -0
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +110 -54
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +110 -54
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +155 -99
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +109 -53
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +108 -52
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +107 -70
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +115 -59
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +115 -59
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +117 -61
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +119 -63
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +125 -69
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +128 -72
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +116 -60
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +113 -57
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +113 -57
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +110 -54
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +108 -52
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +108 -52
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +110 -54
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +113 -57
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +110 -54
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +107 -51
- nautobot/project-static/docs/user-guide/index.html +109 -53
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +113 -57
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +128 -72
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +125 -69
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +125 -69
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +128 -72
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +114 -58
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +113 -57
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +121 -65
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +109 -53
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +116 -60
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +131 -75
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +149 -93
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +108 -84
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +116 -60
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +119 -63
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +137 -81
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +110 -54
- nautobot/project-static/js/forms.js +2 -1
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/views.py +3 -6
- nautobot/virtualization/forms.py +18 -6
- nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
- nautobot/virtualization/views.py +7 -9
- {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/METADATA +2 -2
- {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/RECORD +323 -320
- nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js +0 -29
- nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js.map +0 -7
- nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +0 -1
- {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/NOTICE +0 -0
- {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/WHEEL +0 -0
- {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py
CHANGED
|
@@ -199,6 +199,7 @@ class LocationTypeView(generic.ObjectView):
|
|
|
199
199
|
return {
|
|
200
200
|
"children_table": children_table,
|
|
201
201
|
"locations_table": locations_table,
|
|
202
|
+
**super().get_extra_context(request, instance),
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
|
|
@@ -297,6 +298,7 @@ class LocationView(generic.ObjectView):
|
|
|
297
298
|
"contact_association_permission": ["extras.add_contactassociation"],
|
|
298
299
|
# show the button if any of these fields have non-empty value.
|
|
299
300
|
"show_convert_to_contact_button": instance.contact_name or instance.contact_phone or instance.contact_email,
|
|
301
|
+
**super().get_extra_context(request, instance),
|
|
300
302
|
}
|
|
301
303
|
|
|
302
304
|
|
|
@@ -494,9 +496,7 @@ class RackGroupView(generic.ObjectView):
|
|
|
494
496
|
}
|
|
495
497
|
RequestConfig(request, paginate).configure(rack_table)
|
|
496
498
|
|
|
497
|
-
return {
|
|
498
|
-
"rack_table": rack_table,
|
|
499
|
-
}
|
|
499
|
+
return {"rack_table": rack_table, **super().get_extra_context(request, instance)}
|
|
500
500
|
|
|
501
501
|
|
|
502
502
|
class RackGroupEditView(generic.ObjectEditView):
|
|
@@ -615,6 +615,7 @@ class RackView(generic.ObjectView):
|
|
|
615
615
|
"nonracked_devices": nonracked_devices,
|
|
616
616
|
"next_rack": next_rack,
|
|
617
617
|
"prev_rack": prev_rack,
|
|
618
|
+
**super().get_extra_context(request, instance),
|
|
618
619
|
}
|
|
619
620
|
|
|
620
621
|
|
|
@@ -709,6 +710,7 @@ class ManufacturerListView(generic.ObjectListView):
|
|
|
709
710
|
platform_count=count_related(Platform, "manufacturer"),
|
|
710
711
|
)
|
|
711
712
|
filterset = filters.ManufacturerFilterSet
|
|
713
|
+
filterset_form = forms.ManufacturerFilterForm
|
|
712
714
|
table = tables.ManufacturerTable
|
|
713
715
|
|
|
714
716
|
|
|
@@ -731,9 +733,7 @@ class ManufacturerView(generic.ObjectView):
|
|
|
731
733
|
}
|
|
732
734
|
RequestConfig(request, paginate).configure(device_table)
|
|
733
735
|
|
|
734
|
-
return {
|
|
735
|
-
"device_table": device_table,
|
|
736
|
-
}
|
|
736
|
+
return {"device_table": device_table, **super().get_extra_context(request, instance)}
|
|
737
737
|
|
|
738
738
|
|
|
739
739
|
class ManufacturerEditView(generic.ObjectEditView):
|
|
@@ -839,6 +839,7 @@ class DeviceTypeView(generic.ObjectView):
|
|
|
839
839
|
"rear_port_table": rear_port_table,
|
|
840
840
|
"devicebay_table": devicebay_table,
|
|
841
841
|
"software_image_files_table": software_image_files_table,
|
|
842
|
+
**super().get_extra_context(request, instance),
|
|
842
843
|
}
|
|
843
844
|
|
|
844
845
|
|
|
@@ -1212,6 +1213,7 @@ class PlatformListView(generic.ObjectListView):
|
|
|
1212
1213
|
virtual_machine_count=count_related(VirtualMachine, "platform"),
|
|
1213
1214
|
)
|
|
1214
1215
|
filterset = filters.PlatformFilterSet
|
|
1216
|
+
filterset_form = forms.PlatformFilterForm
|
|
1215
1217
|
table = tables.PlatformTable
|
|
1216
1218
|
|
|
1217
1219
|
|
|
@@ -1237,6 +1239,7 @@ class PlatformView(generic.ObjectView):
|
|
|
1237
1239
|
return {
|
|
1238
1240
|
"device_table": device_table,
|
|
1239
1241
|
"network_driver_tool_names": get_network_driver_mapping_tool_names(),
|
|
1242
|
+
**super().get_extra_context(request, instance),
|
|
1240
1243
|
}
|
|
1241
1244
|
|
|
1242
1245
|
|
|
@@ -1246,7 +1249,10 @@ class PlatformEditView(generic.ObjectEditView):
|
|
|
1246
1249
|
template_name = "dcim/platform_edit.html"
|
|
1247
1250
|
|
|
1248
1251
|
def get_extra_context(self, request, instance):
|
|
1249
|
-
return {
|
|
1252
|
+
return {
|
|
1253
|
+
"network_driver_names": sorted(get_all_network_driver_mappings().keys()),
|
|
1254
|
+
**super().get_extra_context(request, instance),
|
|
1255
|
+
}
|
|
1250
1256
|
|
|
1251
1257
|
|
|
1252
1258
|
class PlatformDeleteView(generic.ObjectDeleteView):
|
|
@@ -1637,7 +1643,7 @@ class ConsolePortView(generic.ObjectView):
|
|
|
1637
1643
|
queryset = ConsolePort.objects.all()
|
|
1638
1644
|
|
|
1639
1645
|
def get_extra_context(self, request, instance):
|
|
1640
|
-
return {"breadcrumb_url": "dcim:device_consoleports"}
|
|
1646
|
+
return {"breadcrumb_url": "dcim:device_consoleports", **super().get_extra_context(request, instance)}
|
|
1641
1647
|
|
|
1642
1648
|
|
|
1643
1649
|
class ConsolePortCreateView(generic.ComponentCreateView):
|
|
@@ -1699,7 +1705,7 @@ class ConsoleServerPortView(generic.ObjectView):
|
|
|
1699
1705
|
queryset = ConsoleServerPort.objects.all()
|
|
1700
1706
|
|
|
1701
1707
|
def get_extra_context(self, request, instance):
|
|
1702
|
-
return {"breadcrumb_url": "dcim:device_consoleserverports"}
|
|
1708
|
+
return {"breadcrumb_url": "dcim:device_consoleserverports", **super().get_extra_context(request, instance)}
|
|
1703
1709
|
|
|
1704
1710
|
|
|
1705
1711
|
class ConsoleServerPortCreateView(generic.ComponentCreateView):
|
|
@@ -1761,7 +1767,7 @@ class PowerPortView(generic.ObjectView):
|
|
|
1761
1767
|
queryset = PowerPort.objects.all()
|
|
1762
1768
|
|
|
1763
1769
|
def get_extra_context(self, request, instance):
|
|
1764
|
-
return {"breadcrumb_url": "dcim:device_powerports"}
|
|
1770
|
+
return {"breadcrumb_url": "dcim:device_powerports", **super().get_extra_context(request, instance)}
|
|
1765
1771
|
|
|
1766
1772
|
|
|
1767
1773
|
class PowerPortCreateView(generic.ComponentCreateView):
|
|
@@ -1823,7 +1829,7 @@ class PowerOutletView(generic.ObjectView):
|
|
|
1823
1829
|
queryset = PowerOutlet.objects.all()
|
|
1824
1830
|
|
|
1825
1831
|
def get_extra_context(self, request, instance):
|
|
1826
|
-
return {"breadcrumb_url": "dcim:device_poweroutlets"}
|
|
1832
|
+
return {"breadcrumb_url": "dcim:device_poweroutlets", **super().get_extra_context(request, instance)}
|
|
1827
1833
|
|
|
1828
1834
|
|
|
1829
1835
|
class PowerOutletCreateView(generic.ComponentCreateView):
|
|
@@ -1919,6 +1925,7 @@ class InterfaceView(generic.ObjectView):
|
|
|
1919
1925
|
"breadcrumb_url": "dcim:device_interfaces",
|
|
1920
1926
|
"child_interfaces_table": child_interfaces_tables,
|
|
1921
1927
|
"redundancy_table": redundancy_table,
|
|
1928
|
+
**super().get_extra_context(request, instance),
|
|
1922
1929
|
}
|
|
1923
1930
|
|
|
1924
1931
|
def _get_interface_redundancy_groups_table(self, request, instance):
|
|
@@ -2005,7 +2012,7 @@ class FrontPortView(generic.ObjectView):
|
|
|
2005
2012
|
queryset = FrontPort.objects.all()
|
|
2006
2013
|
|
|
2007
2014
|
def get_extra_context(self, request, instance):
|
|
2008
|
-
return {"breadcrumb_url": "dcim:device_frontports"}
|
|
2015
|
+
return {"breadcrumb_url": "dcim:device_frontports", **super().get_extra_context(request, instance)}
|
|
2009
2016
|
|
|
2010
2017
|
|
|
2011
2018
|
class FrontPortCreateView(generic.ComponentCreateView):
|
|
@@ -2067,7 +2074,7 @@ class RearPortView(generic.ObjectView):
|
|
|
2067
2074
|
queryset = RearPort.objects.all()
|
|
2068
2075
|
|
|
2069
2076
|
def get_extra_context(self, request, instance):
|
|
2070
|
-
return {"breadcrumb_url": "dcim:device_rearports"}
|
|
2077
|
+
return {"breadcrumb_url": "dcim:device_rearports", **super().get_extra_context(request, instance)}
|
|
2071
2078
|
|
|
2072
2079
|
|
|
2073
2080
|
class RearPortCreateView(generic.ComponentCreateView):
|
|
@@ -2129,7 +2136,7 @@ class DeviceBayView(generic.ObjectView):
|
|
|
2129
2136
|
queryset = DeviceBay.objects.all()
|
|
2130
2137
|
|
|
2131
2138
|
def get_extra_context(self, request, instance):
|
|
2132
|
-
return {"breadcrumb_url": "dcim:device_devicebays"}
|
|
2139
|
+
return {"breadcrumb_url": "dcim:device_devicebays", **super().get_extra_context(request, instance)}
|
|
2133
2140
|
|
|
2134
2141
|
|
|
2135
2142
|
class DeviceBayCreateView(generic.ComponentCreateView):
|
|
@@ -2281,6 +2288,7 @@ class InventoryItemView(generic.ObjectView):
|
|
|
2281
2288
|
return {
|
|
2282
2289
|
"breadcrumb_url": "dcim:device_inventory",
|
|
2283
2290
|
"software_version_images": software_version_images,
|
|
2291
|
+
**super().get_extra_context(request, instance),
|
|
2284
2292
|
}
|
|
2285
2293
|
|
|
2286
2294
|
|
|
@@ -2488,6 +2496,7 @@ class PathTraceView(generic.ObjectView):
|
|
|
2488
2496
|
"path": path,
|
|
2489
2497
|
"related_paths": related_paths,
|
|
2490
2498
|
"total_length": path.get_total_length() if path else None,
|
|
2499
|
+
**super().get_extra_context(request, instance),
|
|
2491
2500
|
}
|
|
2492
2501
|
|
|
2493
2502
|
|
|
@@ -2698,9 +2707,7 @@ class VirtualChassisView(generic.ObjectView):
|
|
|
2698
2707
|
def get_extra_context(self, request, instance):
|
|
2699
2708
|
members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
|
|
2700
2709
|
|
|
2701
|
-
return {
|
|
2702
|
-
"members": members,
|
|
2703
|
-
}
|
|
2710
|
+
return {"members": members, **super().get_extra_context(request, instance)}
|
|
2704
2711
|
|
|
2705
2712
|
|
|
2706
2713
|
class VirtualChassisCreateView(generic.ObjectEditView):
|
|
@@ -2938,9 +2945,7 @@ class PowerPanelView(generic.ObjectView):
|
|
|
2938
2945
|
powerfeed_table = tables.PowerFeedTable(data=power_feeds, orderable=False)
|
|
2939
2946
|
powerfeed_table.exclude = ["power_panel"]
|
|
2940
2947
|
|
|
2941
|
-
return {
|
|
2942
|
-
"powerfeed_table": powerfeed_table,
|
|
2943
|
-
}
|
|
2948
|
+
return {"powerfeed_table": powerfeed_table, **super().get_extra_context(request, instance)}
|
|
2944
2949
|
|
|
2945
2950
|
|
|
2946
2951
|
class PowerPanelEditView(generic.ObjectEditView):
|
|
@@ -3100,6 +3105,7 @@ class InterfaceRedundancyGroupAssociationUIViewSet(ObjectEditViewMixin, ObjectDe
|
|
|
3100
3105
|
|
|
3101
3106
|
class DeviceFamilyUIViewSet(NautobotUIViewSet):
|
|
3102
3107
|
filterset_class = filters.DeviceFamilyFilterSet
|
|
3108
|
+
filterset_form_class = forms.DeviceFamilyFilterForm
|
|
3103
3109
|
form_class = forms.DeviceFamilyForm
|
|
3104
3110
|
bulk_update_form_class = forms.DeviceFamilyBulkEditForm
|
|
3105
3111
|
queryset = DeviceFamily.objects.annotate(device_type_count=count_related(DeviceType, "device_family"))
|
|
@@ -223,17 +223,28 @@ class ContactAssociationSerializer(NautobotModelSerializer):
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
def validate(self, data):
|
|
226
|
-
# Validate uniqueness of (contact/team, role)
|
|
226
|
+
# Validate uniqueness of (associated object, associated object type, contact/team, role)
|
|
227
|
+
unique_together_fields = None
|
|
228
|
+
|
|
227
229
|
if data.get("contact") and data.get("role"):
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
230
|
+
unique_together_fields = (
|
|
231
|
+
"associated_object_type",
|
|
232
|
+
"associated_object_id",
|
|
233
|
+
"contact",
|
|
234
|
+
"role",
|
|
231
235
|
)
|
|
232
|
-
validator(data, self)
|
|
233
236
|
elif data.get("team") and data.get("role"):
|
|
237
|
+
unique_together_fields = (
|
|
238
|
+
"associated_object_type",
|
|
239
|
+
"associated_object_id",
|
|
240
|
+
"team",
|
|
241
|
+
"role",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if unique_together_fields is not None:
|
|
234
245
|
validator = UniqueTogetherValidator(
|
|
235
246
|
queryset=ContactAssociation.objects.all(),
|
|
236
|
-
fields=
|
|
247
|
+
fields=unique_together_fields,
|
|
237
248
|
)
|
|
238
249
|
validator(data, self)
|
|
239
250
|
|
nautobot/extras/api/views.py
CHANGED
|
@@ -304,13 +304,13 @@ class DynamicGroupViewSet(NotesViewSetMixin, ModelViewSet):
|
|
|
304
304
|
# @extend_schema(methods=["get"], responses={200: member_response})
|
|
305
305
|
@action(detail=True, methods=["get"])
|
|
306
306
|
def members(self, request, pk, *args, **kwargs):
|
|
307
|
-
"""List member objects of
|
|
307
|
+
"""List the member objects of this dynamic group."""
|
|
308
308
|
instance = get_object_or_404(self.queryset, pk=pk)
|
|
309
309
|
|
|
310
310
|
# Retrieve the serializer for the content_type and paginate the results
|
|
311
311
|
member_model_class = instance.content_type.model_class()
|
|
312
312
|
member_serializer_class = get_serializer_for_model(member_model_class)
|
|
313
|
-
members = self.paginate_queryset(instance.members)
|
|
313
|
+
members = self.paginate_queryset(instance.members.restrict(request.user, "view"))
|
|
314
314
|
member_serializer = member_serializer_class(members, many=True, context={"request": request})
|
|
315
315
|
return self.get_paginated_response(member_serializer.data)
|
|
316
316
|
|
|
@@ -91,9 +91,12 @@ class ChangeContext:
|
|
|
91
91
|
for entry in self.deferred_object_changes[key]:
|
|
92
92
|
objectchange = entry["instance"].to_objectchange(entry["action"])
|
|
93
93
|
objectchange.user = entry["user"]
|
|
94
|
+
objectchange.user_name = objectchange.user.username
|
|
94
95
|
objectchange.request_id = self.change_id
|
|
95
96
|
objectchange.change_context = self.context
|
|
96
97
|
objectchange.change_context_detail = self.context_detail[:CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL]
|
|
98
|
+
if not objectchange.changed_object_id:
|
|
99
|
+
objectchange.changed_object_id = entry.get("changed_object_id")
|
|
97
100
|
create_object_changes.append(objectchange)
|
|
98
101
|
self.deferred_object_changes.pop(key, None)
|
|
99
102
|
ObjectChange.objects.bulk_create(create_object_changes, batch_size=batch_size)
|
|
@@ -504,7 +504,7 @@ class ContactFilterSet(ContactTeamFilterSet):
|
|
|
504
504
|
fields = "__all__"
|
|
505
505
|
|
|
506
506
|
|
|
507
|
-
class ContactAssociationFilterSet(NautobotFilterSet):
|
|
507
|
+
class ContactAssociationFilterSet(NautobotFilterSet, StatusModelFilterSetMixin, RoleModelFilterSetMixin):
|
|
508
508
|
q = SearchFilter(
|
|
509
509
|
filter_predicates={
|
|
510
510
|
"contact__name": "icontains",
|
|
@@ -512,6 +512,19 @@ class ContactAssociationFilterSet(NautobotFilterSet):
|
|
|
512
512
|
},
|
|
513
513
|
)
|
|
514
514
|
|
|
515
|
+
contact = NaturalKeyOrPKMultipleChoiceFilter(
|
|
516
|
+
queryset=Contact.objects.all(),
|
|
517
|
+
to_field_name="name",
|
|
518
|
+
label="Contact (name or ID)",
|
|
519
|
+
)
|
|
520
|
+
team = NaturalKeyOrPKMultipleChoiceFilter(
|
|
521
|
+
queryset=Team.objects.all(),
|
|
522
|
+
to_field_name="name",
|
|
523
|
+
label="Team (name or ID)",
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
associated_object_type = ContentTypeFilter()
|
|
527
|
+
|
|
515
528
|
class Meta:
|
|
516
529
|
model = ContactAssociation
|
|
517
530
|
fields = "__all__"
|
|
@@ -610,6 +623,7 @@ class ExportTemplateFilterSet(BaseFilterSet):
|
|
|
610
623
|
},
|
|
611
624
|
)
|
|
612
625
|
owner_content_type = ContentTypeFilter()
|
|
626
|
+
content_type = ContentTypeFilter()
|
|
613
627
|
|
|
614
628
|
class Meta:
|
|
615
629
|
model = ExportTemplate
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -104,6 +104,7 @@ __all__ = (
|
|
|
104
104
|
"ConfigContextSchemaBulkEditForm",
|
|
105
105
|
"ConfigContextSchemaFilterForm",
|
|
106
106
|
"CustomFieldForm",
|
|
107
|
+
"CustomFieldFilterForm",
|
|
107
108
|
"CustomFieldModelCSVForm",
|
|
108
109
|
"CustomFieldBulkCreateForm", # 2.0 TODO remove this deprecated class
|
|
109
110
|
"CustomFieldChoiceFormSet",
|
|
@@ -114,6 +115,7 @@ __all__ = (
|
|
|
114
115
|
"DynamicGroupMembershipFormSet",
|
|
115
116
|
"ExportTemplateForm",
|
|
116
117
|
"ExportTemplateFilterForm",
|
|
118
|
+
"ExternalIntegrationFilterForm",
|
|
117
119
|
"ExternalIntegrationForm",
|
|
118
120
|
"ExternalIntegrationBulkEditForm",
|
|
119
121
|
"GitRepositoryForm",
|
|
@@ -144,6 +146,7 @@ __all__ = (
|
|
|
144
146
|
"RelationshipFilterForm",
|
|
145
147
|
"RelationshipAssociationFilterForm",
|
|
146
148
|
"RoleBulkEditForm",
|
|
149
|
+
"RoleFilterForm",
|
|
147
150
|
"RoleForm",
|
|
148
151
|
"ScheduledJobFilterForm",
|
|
149
152
|
"SecretForm",
|
|
@@ -421,6 +424,17 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
421
424
|
self.fields["key"].widget.attrs["readonly"] = True
|
|
422
425
|
|
|
423
426
|
|
|
427
|
+
class CustomFieldFilterForm(NautobotFilterForm):
|
|
428
|
+
model = CustomField
|
|
429
|
+
q = forms.CharField(required=False, label="Search")
|
|
430
|
+
content_types = MultipleContentTypeField(
|
|
431
|
+
queryset=ContentType.objects.filter(FeatureQuery("custom_fields").get_query()),
|
|
432
|
+
choices_as_strings=True,
|
|
433
|
+
required=False,
|
|
434
|
+
label="Content Type(s)",
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
|
|
424
438
|
class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelFormMixin):
|
|
425
439
|
"""
|
|
426
440
|
Base class for CSV/JSON/YAML import of models that support custom fields.
|
|
@@ -638,6 +652,14 @@ class ExternalIntegrationBulkEditForm(NautobotBulkEditForm):
|
|
|
638
652
|
nullable_fields = ["extra_config", "secrets_group", "headers"]
|
|
639
653
|
|
|
640
654
|
|
|
655
|
+
class ExternalIntegrationFilterForm(NautobotFilterForm):
|
|
656
|
+
model = ExternalIntegration
|
|
657
|
+
q = forms.CharField(required=False, label="Search")
|
|
658
|
+
secrets_group = DynamicModelMultipleChoiceField(
|
|
659
|
+
queryset=SecretsGroup.objects.all(), to_field_name="name", required=False
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
|
|
641
663
|
#
|
|
642
664
|
# Git repositories and other data sources
|
|
643
665
|
#
|
|
@@ -1429,6 +1451,17 @@ class RoleBulkEditForm(NautobotBulkEditForm):
|
|
|
1429
1451
|
nullable_fields = ["weight"]
|
|
1430
1452
|
|
|
1431
1453
|
|
|
1454
|
+
class RoleFilterForm(NautobotFilterForm):
|
|
1455
|
+
model = Role
|
|
1456
|
+
q = forms.CharField(required=False, label="Search")
|
|
1457
|
+
content_types = MultipleContentTypeField(
|
|
1458
|
+
queryset=RoleModelsQuery().as_queryset(),
|
|
1459
|
+
required=False,
|
|
1460
|
+
choices_as_strings=True,
|
|
1461
|
+
label="Content Type(s)",
|
|
1462
|
+
)
|
|
1463
|
+
|
|
1464
|
+
|
|
1432
1465
|
#
|
|
1433
1466
|
# Secrets
|
|
1434
1467
|
#
|
nautobot/extras/forms/mixins.py
CHANGED
|
@@ -45,7 +45,6 @@ __all__ = (
|
|
|
45
45
|
# 2.0 TODO: remove the below deprecated aliases
|
|
46
46
|
"AddRemoveTagsForm",
|
|
47
47
|
"CustomFieldBulkEditForm",
|
|
48
|
-
"CustomFieldFilterForm",
|
|
49
48
|
"CustomFieldModelForm",
|
|
50
49
|
"RelationshipModelForm",
|
|
51
50
|
"RoleModelBulkEditFormMixin",
|
|
@@ -767,11 +766,6 @@ class CustomFieldBulkEditForm(CustomFieldModelBulkEditFormMixin):
|
|
|
767
766
|
pass
|
|
768
767
|
|
|
769
768
|
|
|
770
|
-
@class_deprecated_in_favor_of(CustomFieldModelFilterFormMixin)
|
|
771
|
-
class CustomFieldFilterForm(CustomFieldModelFilterFormMixin):
|
|
772
|
-
pass
|
|
773
|
-
|
|
774
|
-
|
|
775
769
|
@class_deprecated_in_favor_of(CustomFieldModelFormMixin)
|
|
776
770
|
class CustomFieldModelForm(CustomFieldModelFormMixin):
|
|
777
771
|
pass
|
nautobot/extras/signals.py
CHANGED
|
@@ -246,7 +246,12 @@ def _handle_deleted_object(sender, instance, **kwargs):
|
|
|
246
246
|
|
|
247
247
|
if save_new_objectchange:
|
|
248
248
|
change_context.deferred_object_changes.setdefault(unique_object_change_id, []).append(
|
|
249
|
-
{
|
|
249
|
+
{
|
|
250
|
+
"action": ObjectChangeActionChoices.ACTION_DELETE,
|
|
251
|
+
"instance": instance,
|
|
252
|
+
"user": user,
|
|
253
|
+
"changed_object_id": instance.pk,
|
|
254
|
+
}
|
|
250
255
|
)
|
|
251
256
|
if not change_context.defer_object_changes:
|
|
252
257
|
objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
|
|
@@ -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,13 +5,23 @@ 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,
|
|
12
21
|
web_request_context,
|
|
13
22
|
)
|
|
14
23
|
from nautobot.extras.models import Status, Webhook
|
|
24
|
+
from nautobot.extras.utils import bulk_delete_with_bulk_change_logging
|
|
15
25
|
|
|
16
26
|
# Use the proper swappable User model
|
|
17
27
|
User = get_user_model()
|
|
@@ -231,6 +241,23 @@ class BulkEditDeleteChangeLogging(TestCase):
|
|
|
231
241
|
location.save()
|
|
232
242
|
location.delete()
|
|
233
243
|
|
|
244
|
+
def test_bulk_delete_has_user_in_change_log(self):
|
|
245
|
+
"""Test that the bulk delete operation adds the user to the change log"""
|
|
246
|
+
location_type = LocationType.objects.get(name="Campus")
|
|
247
|
+
location_status = Status.objects.get_for_model(Location).first()
|
|
248
|
+
with web_request_context(self.user):
|
|
249
|
+
location = Location(name="Test Location 1", location_type=location_type, status=location_status)
|
|
250
|
+
location.save()
|
|
251
|
+
location_pk = location.pk
|
|
252
|
+
location_qs = Location.objects.filter(pk=location_pk)
|
|
253
|
+
bulk_delete_with_bulk_change_logging(location_qs)
|
|
254
|
+
|
|
255
|
+
oc_list = get_changes_for_model(location)
|
|
256
|
+
self.assertEqual(len(oc_list), 2)
|
|
257
|
+
self.assertEqual(oc_list[0].action, ObjectChangeActionChoices.ACTION_DELETE)
|
|
258
|
+
self.assertEqual(oc_list[0].user, self.user)
|
|
259
|
+
self.assertEqual(oc_list[0].user_name, self.user.username)
|
|
260
|
+
|
|
234
261
|
def test_create_then_update(self):
|
|
235
262
|
"""Test that a create followed by an update is logged as a single create"""
|
|
236
263
|
location_type = LocationType.objects.get(name="Campus")
|
|
@@ -276,6 +303,29 @@ class BulkEditDeleteChangeLogging(TestCase):
|
|
|
276
303
|
self.assertIsNone(snapshots["differences"]["removed"])
|
|
277
304
|
self.assertEqual(snapshots["differences"]["added"]["description"], "changed")
|
|
278
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
|
+
|
|
279
329
|
def test_change_log_context(self):
|
|
280
330
|
location_type = LocationType.objects.get(name="Campus")
|
|
281
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),
|