nautobot 2.4.1__py3-none-any.whl → 2.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
- nautobot/circuits/tests/integration/test_relationships.py +1 -1
- nautobot/core/apps/__init__.py +0 -5
- nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
- nautobot/core/testing/integration.py +437 -10
- nautobot/core/tests/test_jobs.py +34 -2
- nautobot/core/utils/git.py +7 -2
- nautobot/core/views/generic.py +1 -1
- nautobot/core/views/mixins.py +13 -6
- nautobot/core/views/utils.py +2 -2
- nautobot/dcim/forms.py +12 -0
- nautobot/dcim/tables/devices.py +2 -1
- nautobot/dcim/templates/dcim/cable.html +1 -1
- nautobot/dcim/templates/dcim/device/base.html +1 -1
- nautobot/dcim/templates/dcim/device.html +2 -2
- nautobot/dcim/templates/dcim/device_component.html +1 -1
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/locationtype.html +1 -1
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/manufacturer.html +1 -1
- nautobot/dcim/templates/dcim/platform.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rackgroup.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation.html +2 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
- nautobot/dcim/tests/test_views.py +9 -1
- nautobot/dcim/views.py +12 -15
- nautobot/extras/api/serializers.py +33 -0
- nautobot/extras/api/views.py +11 -3
- nautobot/extras/constants.py +1 -0
- nautobot/extras/datasources/git.py +125 -0
- nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
- nautobot/extras/models/customfields.py +29 -12
- nautobot/extras/models/datasources.py +85 -0
- nautobot/extras/models/models.py +15 -0
- nautobot/extras/models/relationships.py +17 -5
- nautobot/extras/signals.py +15 -1
- nautobot/extras/templates/extras/computedfield.html +1 -1
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/customfield.html +1 -1
- nautobot/extras/templates/extras/customlink.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/exporttemplate.html +1 -1
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/graphqlquery.html +1 -1
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
- nautobot/extras/templates/extras/jobhook.html +1 -1
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/plugin_detail.html +1 -1
- nautobot/extras/templates/extras/relationship.html +1 -63
- nautobot/extras/templates/extras/role_retrieve.html +1 -1
- nautobot/extras/templates/extras/scheduledjob.html +1 -1
- nautobot/extras/templates/extras/secret.html +1 -1
- nautobot/extras/templates/extras/secretsgroup.html +1 -1
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/webhook.html +1 -1
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
- nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
- nautobot/extras/tests/git_helper.py +9 -1
- nautobot/extras/tests/integration/__init__.py +29 -16
- nautobot/extras/tests/test_api.py +6 -0
- nautobot/extras/tests/test_customfields.py +49 -51
- nautobot/extras/tests/test_datasources.py +27 -0
- nautobot/extras/tests/test_models.py +283 -0
- nautobot/extras/tests/test_utils.py +22 -1
- nautobot/extras/utils.py +17 -8
- nautobot/extras/views.py +55 -12
- nautobot/ipam/models.py +8 -2
- nautobot/ipam/tables.py +2 -2
- nautobot/ipam/templates/ipam/ipaddress.html +1 -1
- nautobot/ipam/templates/ipam/prefix.html +1 -1
- nautobot/ipam/templates/ipam/rir.html +1 -1
- nautobot/ipam/templates/ipam/routetarget.html +1 -1
- nautobot/ipam/templates/ipam/service.html +1 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlangroup.html +1 -1
- nautobot/ipam/templates/ipam/vrf.html +1 -1
- nautobot/ipam/tests/test_models.py +24 -0
- nautobot/ipam/tests/test_utils.py +41 -2
- nautobot/ipam/utils/__init__.py +18 -11
- nautobot/project-static/docs/404.html +87 -12
- nautobot/project-static/docs/apps/index.html +87 -12
- nautobot/project-static/docs/apps/nautobot-apps.html +87 -12
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
- nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/graphql.html +87 -12
- nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
- nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
- nautobot/project-static/docs/development/apps/api/setup.html +87 -12
- nautobot/project-static/docs/development/apps/api/testing.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
- nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
- nautobot/project-static/docs/development/apps/index.html +87 -12
- nautobot/project-static/docs/development/apps/migration/code-updates.html +87 -12
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +87 -12
- nautobot/project-static/docs/development/apps/migration/from-v1.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
- nautobot/project-static/docs/development/core/application-registry.html +87 -12
- nautobot/project-static/docs/development/core/best-practices.html +87 -12
- nautobot/project-static/docs/development/core/bootstrap-ui.html +87 -12
- nautobot/project-static/docs/development/core/caching.html +87 -12
- nautobot/project-static/docs/development/core/controllers.html +87 -12
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +87 -12
- nautobot/project-static/docs/development/core/generic-views.html +87 -12
- nautobot/project-static/docs/development/core/getting-started.html +87 -12
- nautobot/project-static/docs/development/core/homepage.html +87 -12
- nautobot/project-static/docs/development/core/index.html +87 -12
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +87 -12
- nautobot/project-static/docs/development/core/model-checklist.html +87 -12
- nautobot/project-static/docs/development/core/model-features.html +87 -12
- nautobot/project-static/docs/development/core/natural-keys.html +87 -12
- nautobot/project-static/docs/development/core/navigation-menu.html +87 -12
- nautobot/project-static/docs/development/core/release-checklist.html +87 -12
- nautobot/project-static/docs/development/core/role-internals.html +87 -12
- nautobot/project-static/docs/development/core/settings.html +87 -12
- nautobot/project-static/docs/development/core/style-guide.html +87 -12
- nautobot/project-static/docs/development/core/templates.html +88 -13
- nautobot/project-static/docs/development/core/testing.html +87 -12
- nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
- nautobot/project-static/docs/development/core/user-preferences.html +87 -12
- nautobot/project-static/docs/development/index.html +87 -12
- nautobot/project-static/docs/development/jobs/index.html +87 -12
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +87 -12
- nautobot/project-static/docs/index.html +87 -12
- nautobot/project-static/docs/overview/application_stack.html +87 -12
- nautobot/project-static/docs/overview/design_philosophy.html +87 -12
- nautobot/project-static/docs/release-notes/index.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.0.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.1.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.2.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.3.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.4.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.5.html +87 -12
- nautobot/project-static/docs/release-notes/version-1.6.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.0.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.1.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.2.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.3.html +87 -12
- nautobot/project-static/docs/release-notes/version-2.4.html +277 -12
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +296 -288
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +87 -12
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +90 -15
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +87 -12
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +87 -12
- nautobot/project-static/docs/user-guide/administration/installation/services.html +87 -12
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
- nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
- nautobot/project-static/docs/user-guide/administration/security/notices.html +9843 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +87 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +187 -29
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
- nautobot/project-static/docs/user-guide/index.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
- nautobot/tenancy/templates/tenancy/tenant.html +1 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- nautobot/virtualization/templates/virtualization/cluster.html +1 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
- nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/METADATA +5 -5
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/RECORD +404 -397
- nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
- nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
- /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/NOTICE +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/WHEEL +0 -0
- {nautobot-2.4.1.dist-info → nautobot-2.4.2.dist-info}/entry_points.txt +0 -0
nautobot/core/views/generic.py
CHANGED
|
@@ -1051,7 +1051,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAnd
|
|
|
1051
1051
|
|
|
1052
1052
|
if form.is_valid():
|
|
1053
1053
|
logger.debug("Form validation was successful")
|
|
1054
|
-
return self.send_bulk_edit_objects_to_job(request, form, model)
|
|
1054
|
+
return self.send_bulk_edit_objects_to_job(request, form.cleaned_data, model)
|
|
1055
1055
|
else:
|
|
1056
1056
|
logger.debug("Form validation failed")
|
|
1057
1057
|
|
nautobot/core/views/mixins.py
CHANGED
|
@@ -992,10 +992,9 @@ class BulkEditAndBulkDeleteModelMixin:
|
|
|
992
992
|
)
|
|
993
993
|
return redirect("extras:jobresult", pk=job_result.pk)
|
|
994
994
|
|
|
995
|
-
def send_bulk_edit_objects_to_job(self, request,
|
|
995
|
+
def send_bulk_edit_objects_to_job(self, request, form_data, model):
|
|
996
996
|
"""Prepare and enqueue a bulk edit job."""
|
|
997
997
|
job_model = Job.objects.get_for_class_path(BulkEditObjects.class_path)
|
|
998
|
-
form_data = normalize_querydict(request.POST, form)
|
|
999
998
|
if filterset_class := lookup.get_filterset_for_model(model):
|
|
1000
999
|
filter_query_params = normalize_querydict(request.GET, filterset=filterset_class())
|
|
1001
1000
|
else:
|
|
@@ -1288,7 +1287,7 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin, Bulk
|
|
|
1288
1287
|
form = form_class(queryset.model, request.POST, edit_all=edit_all)
|
|
1289
1288
|
restrict_form_fields(form, request.user)
|
|
1290
1289
|
if form.is_valid():
|
|
1291
|
-
return self.send_bulk_edit_objects_to_job(self.request, form, queryset.model)
|
|
1290
|
+
return self.send_bulk_edit_objects_to_job(self.request, form.cleaned_data, queryset.model)
|
|
1292
1291
|
else:
|
|
1293
1292
|
return self.form_invalid(form)
|
|
1294
1293
|
table = None
|
|
@@ -1312,10 +1311,14 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin, Bulk
|
|
|
1312
1311
|
|
|
1313
1312
|
class ObjectChangeLogViewMixin(NautobotViewSetMixin):
|
|
1314
1313
|
"""
|
|
1315
|
-
UI mixin to list a model's changelog queryset
|
|
1314
|
+
UI mixin to list a model's changelog queryset.
|
|
1315
|
+
|
|
1316
|
+
base_template: Specify to explicitly identify the base object detail template to render.
|
|
1317
|
+
If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
|
|
1318
|
+
will be used, as per `get_base_template()`.
|
|
1316
1319
|
"""
|
|
1317
1320
|
|
|
1318
|
-
base_template = None
|
|
1321
|
+
base_template: Optional[str] = None
|
|
1319
1322
|
|
|
1320
1323
|
@drf_action(detail=True)
|
|
1321
1324
|
def changelog(self, request, *args, **kwargs):
|
|
@@ -1330,9 +1333,13 @@ class ObjectChangeLogViewMixin(NautobotViewSetMixin):
|
|
|
1330
1333
|
class ObjectNotesViewMixin(NautobotViewSetMixin):
|
|
1331
1334
|
"""
|
|
1332
1335
|
UI Mixin for an Object's Notes.
|
|
1336
|
+
|
|
1337
|
+
base_template: Specify to explicitly identify the base object detail template to render.
|
|
1338
|
+
If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
|
|
1339
|
+
will be used, as per `get_base_template()`.
|
|
1333
1340
|
"""
|
|
1334
1341
|
|
|
1335
|
-
base_template = None
|
|
1342
|
+
base_template: Optional[str] = None
|
|
1336
1343
|
|
|
1337
1344
|
@drf_action(detail=True)
|
|
1338
1345
|
def notes(self, request, *args, **kwargs):
|
nautobot/core/views/utils.py
CHANGED
|
@@ -137,10 +137,10 @@ def get_csv_form_fields_from_serializer_class(serializer_class):
|
|
|
137
137
|
elif cf.type == CustomFieldTypeChoices.TYPE_DATE:
|
|
138
138
|
field_info["format"] = mark_safe("<code>YYYY-MM-DD</code>") # noqa: S308
|
|
139
139
|
elif cf.type == CustomFieldTypeChoices.TYPE_SELECT:
|
|
140
|
-
field_info["choices"] = {
|
|
140
|
+
field_info["choices"] = {value: value for value in cf.choices}
|
|
141
141
|
elif cf.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
|
|
142
142
|
field_info["format"] = mark_safe('<code>"value,value"</code>') # noqa: S308
|
|
143
|
-
field_info["choices"] = {
|
|
143
|
+
field_info["choices"] = {value: value for value in cf.choices}
|
|
144
144
|
fields.append(field_info)
|
|
145
145
|
continue
|
|
146
146
|
|
nautobot/dcim/forms.py
CHANGED
|
@@ -307,6 +307,17 @@ class LocationTypeFilterForm(NautobotFilterForm):
|
|
|
307
307
|
content_types = MultipleContentTypeField(feature="locations", choices_as_strings=True, required=False)
|
|
308
308
|
|
|
309
309
|
|
|
310
|
+
class LocationTypeBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
311
|
+
pk = forms.ModelMultipleChoiceField(queryset=LocationType.objects.all(), widget=forms.MultipleHiddenInput())
|
|
312
|
+
description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
|
|
313
|
+
nestable = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
|
|
314
|
+
add_content_types = MultipleContentTypeField(feature="locations", required=False)
|
|
315
|
+
remove_content_types = MultipleContentTypeField(feature="locations", required=False)
|
|
316
|
+
|
|
317
|
+
class Meta:
|
|
318
|
+
nullable_fields = []
|
|
319
|
+
|
|
320
|
+
|
|
310
321
|
#
|
|
311
322
|
# Locations
|
|
312
323
|
#
|
|
@@ -784,6 +795,7 @@ class DeviceFamilyForm(NautobotModelForm):
|
|
|
784
795
|
fields = [
|
|
785
796
|
"name",
|
|
786
797
|
"description",
|
|
798
|
+
"tags",
|
|
787
799
|
]
|
|
788
800
|
|
|
789
801
|
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -106,7 +106,8 @@ __all__ = (
|
|
|
106
106
|
|
|
107
107
|
class PlatformTable(BaseTable):
|
|
108
108
|
pk = ToggleColumn()
|
|
109
|
-
name = tables.
|
|
109
|
+
name = tables.Column(linkify=True)
|
|
110
|
+
manufacturer = tables.Column(linkify=True)
|
|
110
111
|
device_count = LinkedCountColumn(
|
|
111
112
|
viewname="dcim:device_list",
|
|
112
113
|
url_params={"platform": "pk"},
|
|
@@ -214,8 +214,8 @@
|
|
|
214
214
|
<tr>
|
|
215
215
|
<td>Cluster</td>
|
|
216
216
|
<td>
|
|
217
|
-
{% if object.cluster.
|
|
218
|
-
{{ object.cluster.
|
|
217
|
+
{% if object.cluster.cluster_group %}
|
|
218
|
+
{{ object.cluster.cluster_group|hyperlinked_object }} /
|
|
219
219
|
{% endif %}
|
|
220
220
|
{{ object.cluster|hyperlinked_object }}
|
|
221
221
|
</td>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
2
2
|
{% load helpers %}
|
|
3
3
|
{% load static %}
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{% with rack=object.rack %}
|
|
16
16
|
<tr>
|
|
17
17
|
<td>Location</td>
|
|
18
|
-
<td>{% include 'dcim/inc/location_hierarchy.html' with location=
|
|
18
|
+
<td>{% include 'dcim/inc/location_hierarchy.html' with location=rack.location %}</td>
|
|
19
19
|
</tr>
|
|
20
20
|
<tr>
|
|
21
21
|
<td>Group</td>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from nautobot.core.testing.integration import (
|
|
4
|
+
BulkOperationsTestCases,
|
|
5
|
+
)
|
|
6
|
+
from nautobot.dcim.models import Device
|
|
7
|
+
from nautobot.extras.tests.integration import create_test_device
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DeviceBulkOperationsTestCase(BulkOperationsTestCases.BulkOperationsTestCase):
|
|
11
|
+
"""
|
|
12
|
+
Test devices bulk edit / delete operations.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
model_menu_path = ("Devices", "Devices")
|
|
16
|
+
model_base_viewname = "dcim:device"
|
|
17
|
+
model_edit_data = {"serial": "Test serial"}
|
|
18
|
+
model_filter_by = {"location": "Test Location 2"}
|
|
19
|
+
model_class = Device
|
|
20
|
+
|
|
21
|
+
def setup_items(self):
|
|
22
|
+
Device.objects.all().delete()
|
|
23
|
+
test_uuid = str(uuid.uuid4())
|
|
24
|
+
|
|
25
|
+
# Create device for test
|
|
26
|
+
create_test_device("Test Device Integration Test 1", test_uuid=test_uuid)
|
|
27
|
+
create_test_device("Test Device Integration Test 2", test_uuid=test_uuid)
|
|
28
|
+
create_test_device("Test Device Integration Test 3", test_uuid=test_uuid)
|
|
29
|
+
create_test_device("Test Device Integration Test 4", "Test Location 2", test_uuid=test_uuid)
|
|
30
|
+
create_test_device("Test Device Integration Test 5", "Test Location 2", test_uuid=test_uuid)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
2
|
+
|
|
3
|
+
from nautobot.core.testing.integration import (
|
|
4
|
+
BulkOperationsTestCases,
|
|
5
|
+
)
|
|
6
|
+
from nautobot.dcim.models import Device, Location, LocationType
|
|
7
|
+
from nautobot.extras.models import Status
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LocationBulkOperationsTestCase(BulkOperationsTestCases.BulkOperationsTestCase):
|
|
11
|
+
"""
|
|
12
|
+
Test locations bulk edit / delete operations.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
model_menu_path = ("Organization", "Locations")
|
|
16
|
+
model_base_viewname = "dcim:location"
|
|
17
|
+
model_edit_data = {"description": "Test description"}
|
|
18
|
+
model_filter_by = {"location_type": "External"}
|
|
19
|
+
model_class = Location
|
|
20
|
+
|
|
21
|
+
def setup_items(self):
|
|
22
|
+
Location.objects.all().delete()
|
|
23
|
+
|
|
24
|
+
# Create locations for test
|
|
25
|
+
self.create_location("Test Location Integration Test 1")
|
|
26
|
+
self.create_location("Test Location Integration Test 2")
|
|
27
|
+
self.create_location("Test Location Integration Test 3")
|
|
28
|
+
self.create_location("Test Location Integration Test 4", "External")
|
|
29
|
+
self.create_location("Test Location Integration Test 5", "External")
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def create_location(location_name, location_type="Internal"):
|
|
33
|
+
location_type, location_type_created = LocationType.objects.get_or_create(name=location_type)
|
|
34
|
+
if location_type_created:
|
|
35
|
+
location_type.content_types.add(ContentType.objects.get_for_model(Device))
|
|
36
|
+
location_type.save()
|
|
37
|
+
|
|
38
|
+
location_status = Status.objects.get_for_model(Location).first()
|
|
39
|
+
Location.objects.get_or_create(
|
|
40
|
+
name=location_name,
|
|
41
|
+
status=location_status,
|
|
42
|
+
location_type=location_type,
|
|
43
|
+
)
|
|
@@ -161,7 +161,7 @@ def create_test_device(name):
|
|
|
161
161
|
return device
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
class LocationTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|
164
|
+
class LocationTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase, ViewTestCases.BulkEditObjectsViewTestCase):
|
|
165
165
|
model = LocationType
|
|
166
166
|
sort_on_field = "nestable"
|
|
167
167
|
|
|
@@ -194,6 +194,13 @@ class LocationTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|
|
194
194
|
"nestable": True,
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
cls.bulk_edit_data = {
|
|
198
|
+
"description": "A generic description",
|
|
199
|
+
"add_content_types": [
|
|
200
|
+
ContentType.objects.get_for_model(CircuitTermination).pk,
|
|
201
|
+
],
|
|
202
|
+
}
|
|
203
|
+
|
|
197
204
|
def _get_queryset(self):
|
|
198
205
|
return super()._get_queryset().order_by("-last_updated")
|
|
199
206
|
|
|
@@ -743,6 +750,7 @@ class DeviceFamilyTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
743
750
|
cls.form_data = {
|
|
744
751
|
"name": "New Device Family",
|
|
745
752
|
"description": "A new device family",
|
|
753
|
+
"tags": [t.pk for t in Tag.objects.get_for_model(DeviceFamily)],
|
|
746
754
|
}
|
|
747
755
|
cls.bulk_edit_data = {
|
|
748
756
|
"description": "A new device family",
|
nautobot/dcim/views.py
CHANGED
|
@@ -40,7 +40,7 @@ from nautobot.core.ui.choices import SectionChoices
|
|
|
40
40
|
from nautobot.core.utils.lookup import get_form_for_model
|
|
41
41
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
42
42
|
from nautobot.core.utils.requests import normalize_querydict
|
|
43
|
-
from nautobot.core.views import generic
|
|
43
|
+
from nautobot.core.views import generic
|
|
44
44
|
from nautobot.core.views.mixins import (
|
|
45
45
|
GetReturnURLMixin,
|
|
46
46
|
ObjectBulkDestroyViewMixin,
|
|
@@ -213,21 +213,13 @@ class BaseDeviceComponentTemplatesBulkRenameView(generic.BulkRenameView):
|
|
|
213
213
|
#
|
|
214
214
|
|
|
215
215
|
|
|
216
|
-
class LocationTypeUIViewSet(
|
|
217
|
-
view_mixins.ObjectDetailViewMixin,
|
|
218
|
-
view_mixins.ObjectListViewMixin,
|
|
219
|
-
view_mixins.ObjectEditViewMixin,
|
|
220
|
-
view_mixins.ObjectDestroyViewMixin,
|
|
221
|
-
view_mixins.ObjectBulkDestroyViewMixin,
|
|
222
|
-
view_mixins.ObjectBulkCreateViewMixin, # 3.0 TODO: remove this mixin as it's no longer used
|
|
223
|
-
view_mixins.ObjectChangeLogViewMixin,
|
|
224
|
-
view_mixins.ObjectNotesViewMixin,
|
|
225
|
-
):
|
|
216
|
+
class LocationTypeUIViewSet(NautobotUIViewSet):
|
|
226
217
|
queryset = LocationType.objects.all()
|
|
227
218
|
filterset_class = filters.LocationTypeFilterSet
|
|
228
219
|
filterset_form_class = forms.LocationTypeFilterForm
|
|
229
220
|
table_class = tables.LocationTypeTable
|
|
230
221
|
form_class = forms.LocationTypeForm
|
|
222
|
+
bulk_update_form_class = forms.LocationTypeBulkEditForm
|
|
231
223
|
serializer_class = serializers.LocationSerializer
|
|
232
224
|
|
|
233
225
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
@@ -1755,16 +1747,21 @@ class DeviceListView(generic.ObjectListView):
|
|
|
1755
1747
|
|
|
1756
1748
|
class DeviceView(generic.ObjectView):
|
|
1757
1749
|
queryset = Device.objects.select_related(
|
|
1750
|
+
"cluster__cluster_group",
|
|
1751
|
+
"controller_managed_device_group__controller",
|
|
1752
|
+
"device_redundancy_group",
|
|
1753
|
+
"device_type__device_family",
|
|
1758
1754
|
"location",
|
|
1759
|
-
"rack__rack_group",
|
|
1760
|
-
"tenant__tenant_group",
|
|
1761
|
-
"role",
|
|
1762
1755
|
"platform",
|
|
1763
1756
|
"primary_ip4",
|
|
1764
1757
|
"primary_ip6",
|
|
1758
|
+
"rack__rack_group",
|
|
1759
|
+
"role",
|
|
1760
|
+
"secrets_group",
|
|
1765
1761
|
"software_version",
|
|
1766
1762
|
"status",
|
|
1767
|
-
|
|
1763
|
+
"tenant__tenant_group",
|
|
1764
|
+
).prefetch_related("images", "software_image_files")
|
|
1768
1765
|
|
|
1769
1766
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
1770
1767
|
extra_buttons=(
|
|
@@ -480,11 +480,32 @@ class GitRepositorySerializer(TaggedModelSerializerMixin, NautobotModelSerialize
|
|
|
480
480
|
|
|
481
481
|
class GraphQLQuerySerializer(ValidatedModelSerializer, NotesSerializerMixin):
|
|
482
482
|
variables = serializers.DictField(read_only=True)
|
|
483
|
+
owner_content_type = ContentTypeField(
|
|
484
|
+
queryset=ContentType.objects.filter(FeatureQuery("graphql_query_owners").get_query()),
|
|
485
|
+
required=False,
|
|
486
|
+
allow_null=True,
|
|
487
|
+
default=None,
|
|
488
|
+
)
|
|
489
|
+
owner = serializers.SerializerMethodField(read_only=True)
|
|
483
490
|
|
|
484
491
|
class Meta:
|
|
485
492
|
model = GraphQLQuery
|
|
486
493
|
fields = "__all__"
|
|
487
494
|
|
|
495
|
+
@extend_schema_field(
|
|
496
|
+
PolymorphicProxySerializer(
|
|
497
|
+
component_name="GraphQLQueryOwner",
|
|
498
|
+
resource_type_field_name="object_type",
|
|
499
|
+
serializers=lambda: nested_serializers_for_models(FeatureQuery("graphql_query_owners").list_subclasses()),
|
|
500
|
+
allow_null=True,
|
|
501
|
+
)
|
|
502
|
+
)
|
|
503
|
+
def get_owner(self, obj):
|
|
504
|
+
if obj.owner is None:
|
|
505
|
+
return None
|
|
506
|
+
depth = get_nested_serializer_depth(self)
|
|
507
|
+
return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.owner, "owner")
|
|
508
|
+
|
|
488
509
|
|
|
489
510
|
class GraphQLQueryInputSerializer(serializers.Serializer):
|
|
490
511
|
variables = serializers.DictField(allow_null=True, default={})
|
|
@@ -1148,3 +1169,15 @@ class WebhookSerializer(ValidatedModelSerializer, NotesSerializerMixin):
|
|
|
1148
1169
|
raise serializers.ValidationError(conflicts)
|
|
1149
1170
|
|
|
1150
1171
|
return validated_attrs
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
#
|
|
1175
|
+
# More Git repositories
|
|
1176
|
+
#
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
class GitRepositorySyncResponseSerializer(serializers.Serializer):
|
|
1180
|
+
"""Serializer representing responses from the GitRepository.sync() POST endpoint."""
|
|
1181
|
+
|
|
1182
|
+
message = serializers.CharField(read_only=True)
|
|
1183
|
+
job_result = JobResultSerializer(read_only=True)
|
nautobot/extras/api/views.py
CHANGED
|
@@ -425,7 +425,7 @@ class GitRepositoryViewSet(NautobotModelViewSet):
|
|
|
425
425
|
serializer_class = serializers.GitRepositorySerializer
|
|
426
426
|
filterset_class = filters.GitRepositoryFilterSet
|
|
427
427
|
|
|
428
|
-
@extend_schema(methods=["post"],
|
|
428
|
+
@extend_schema(methods=["post"], responses={"200": serializers.GitRepositorySyncResponseSerializer}, request=None)
|
|
429
429
|
# Since we are explicitly checking for `extras:change_gitrepository` in the API sync() method
|
|
430
430
|
# We explicitly set the permission_classes to IsAuthenticated in the @action decorator
|
|
431
431
|
# bypassing the default DRF permission check for `extras:add_gitrepository` and the permission check fall through to the function itself.
|
|
@@ -441,8 +441,16 @@ class GitRepositoryViewSet(NautobotModelViewSet):
|
|
|
441
441
|
raise CeleryWorkerNotRunningException()
|
|
442
442
|
|
|
443
443
|
repository = get_object_or_404(GitRepository, id=pk)
|
|
444
|
-
repository.sync(user=request.user)
|
|
445
|
-
|
|
444
|
+
job_result = repository.sync(user=request.user)
|
|
445
|
+
|
|
446
|
+
data = {
|
|
447
|
+
# Kept message for backward compatibility for now
|
|
448
|
+
"message": f"Repository {repository} sync job added to queue.",
|
|
449
|
+
"job_result": job_result,
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
serializer = serializers.GitRepositorySyncResponseSerializer(data, context={"request": request})
|
|
453
|
+
return Response(serializer.data, status=status.HTTP_200_OK)
|
|
446
454
|
|
|
447
455
|
|
|
448
456
|
#
|
nautobot/extras/constants.py
CHANGED
|
@@ -30,6 +30,7 @@ from nautobot.extras.models import (
|
|
|
30
30
|
DynamicGroup,
|
|
31
31
|
ExportTemplate,
|
|
32
32
|
GitRepository,
|
|
33
|
+
GraphQLQuery,
|
|
33
34
|
Job,
|
|
34
35
|
JobQueue,
|
|
35
36
|
JobResult,
|
|
@@ -935,6 +936,120 @@ def delete_git_export_templates(repository_record, job_result, preserve=None):
|
|
|
935
936
|
job_result.log(msg, level_choice=LogLevelChoices.LOG_WARNING, grouping="export templates")
|
|
936
937
|
|
|
937
938
|
|
|
939
|
+
#
|
|
940
|
+
# GraphQL handling
|
|
941
|
+
#
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
def refresh_git_graphql_queries(repository_record, job_result, delete=False):
|
|
945
|
+
"""Callback function for GitRepository updates - refresh all GraphQLQuery managed by this repository."""
|
|
946
|
+
if "extras.graphqlquery" in repository_record.provided_contents and not delete:
|
|
947
|
+
update_git_graphql_queries(repository_record, job_result)
|
|
948
|
+
else:
|
|
949
|
+
delete_git_graphql_queries(repository_record, job_result)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
logger = logging.getLogger(__name__)
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
def update_git_graphql_queries(repository_record, job_result):
|
|
956
|
+
"""Refresh any GraphQL queries provided by this Git repository."""
|
|
957
|
+
graphql_query_path = os.path.join(repository_record.filesystem_path, "graphql_queries")
|
|
958
|
+
git_repository_content_type = ContentType.objects.get_for_model(GitRepository)
|
|
959
|
+
graphql_queries = []
|
|
960
|
+
|
|
961
|
+
if os.path.isdir(graphql_query_path):
|
|
962
|
+
for file in os.listdir(graphql_query_path):
|
|
963
|
+
file_path = os.path.join(graphql_query_path, file)
|
|
964
|
+
if not os.path.isfile(file_path):
|
|
965
|
+
continue
|
|
966
|
+
|
|
967
|
+
# Remove `.gql` extension from the name if it exists
|
|
968
|
+
query_name = file.rsplit(".gql", 1)[0] if file.endswith(".gql") else file
|
|
969
|
+
|
|
970
|
+
try:
|
|
971
|
+
with open(file_path, "r") as fd:
|
|
972
|
+
query_content = fd.read().strip()
|
|
973
|
+
|
|
974
|
+
graphql_query, created = GraphQLQuery.objects.get_or_create(
|
|
975
|
+
name=query_name,
|
|
976
|
+
owner_content_type=git_repository_content_type,
|
|
977
|
+
owner_object_id=repository_record.pk,
|
|
978
|
+
defaults={"query": query_content},
|
|
979
|
+
)
|
|
980
|
+
modified = graphql_query.query != query_content
|
|
981
|
+
graphql_queries.append(query_name)
|
|
982
|
+
# Only attempt to update if the content has changed
|
|
983
|
+
if modified:
|
|
984
|
+
try:
|
|
985
|
+
graphql_query.query = query_content
|
|
986
|
+
graphql_query.validated_save()
|
|
987
|
+
msg = (
|
|
988
|
+
f"Successfully created GraphQL query: {query_name}"
|
|
989
|
+
if created
|
|
990
|
+
else f"Successfully updated GraphQL query: {query_name}"
|
|
991
|
+
)
|
|
992
|
+
logger.info(msg)
|
|
993
|
+
job_result.log(
|
|
994
|
+
msg, obj=graphql_query, level_choice=LogLevelChoices.LOG_INFO, grouping="graphql queries"
|
|
995
|
+
)
|
|
996
|
+
except Exception as exc:
|
|
997
|
+
# Log validation error and retain the existing query
|
|
998
|
+
error_msg = (
|
|
999
|
+
f"Invalid GraphQL syntax for query '{query_name}'. "
|
|
1000
|
+
f"Retaining the existing query. Error: {exc}"
|
|
1001
|
+
)
|
|
1002
|
+
logger.error(error_msg)
|
|
1003
|
+
job_result.log(error_msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="graphql queries")
|
|
1004
|
+
continue
|
|
1005
|
+
else:
|
|
1006
|
+
msg = f"No changes to GraphQL query: {query_name}"
|
|
1007
|
+
logger.info(msg)
|
|
1008
|
+
job_result.log(
|
|
1009
|
+
msg, obj=graphql_query, level_choice=LogLevelChoices.LOG_INFO, grouping="graphql queries"
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
except Exception as exc:
|
|
1013
|
+
# Check if a query with the same name already exists
|
|
1014
|
+
existing_query = GraphQLQuery.objects.filter(name=query_name).first()
|
|
1015
|
+
if existing_query and existing_query.owner_object_id != repository_record.pk:
|
|
1016
|
+
error_msg = (
|
|
1017
|
+
f"GraphQL query '{query_name}' already exists "
|
|
1018
|
+
f"Please rename the query in the repository and try again."
|
|
1019
|
+
)
|
|
1020
|
+
else:
|
|
1021
|
+
error_msg = f"Error processing GraphQL query file '{file}': {exc}"
|
|
1022
|
+
|
|
1023
|
+
# Log the error
|
|
1024
|
+
logger.error(error_msg)
|
|
1025
|
+
job_result.log(error_msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="graphql queries")
|
|
1026
|
+
|
|
1027
|
+
# Delete any queries not in the preserved list
|
|
1028
|
+
delete_git_graphql_queries(repository_record, job_result, preserve=graphql_queries)
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
def delete_git_graphql_queries(repository_record, job_result, preserve=None):
|
|
1032
|
+
"""Delete GraphQL queries owned by the given Git repository that are not in the preserve list."""
|
|
1033
|
+
git_repository_content_type = ContentType.objects.get_for_model(GitRepository)
|
|
1034
|
+
if preserve is None:
|
|
1035
|
+
preserve = []
|
|
1036
|
+
|
|
1037
|
+
for graphql_query in GraphQLQuery.objects.filter(
|
|
1038
|
+
owner_content_type=git_repository_content_type,
|
|
1039
|
+
owner_object_id=repository_record.pk,
|
|
1040
|
+
):
|
|
1041
|
+
if graphql_query.name not in preserve:
|
|
1042
|
+
try:
|
|
1043
|
+
graphql_query.delete()
|
|
1044
|
+
msg = f"Deleted GraphQL query: {graphql_query.name}"
|
|
1045
|
+
logger.warning(msg)
|
|
1046
|
+
job_result.log(msg, level_choice=LogLevelChoices.LOG_WARNING, grouping="graphql queries")
|
|
1047
|
+
except Exception as exc:
|
|
1048
|
+
error_msg = f"Unable to delete '{graphql_query.name}': {exc}"
|
|
1049
|
+
logger.error(error_msg)
|
|
1050
|
+
job_result.log(error_msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="graphql queries")
|
|
1051
|
+
|
|
1052
|
+
|
|
938
1053
|
# Register built-in callbacks for data types potentially provided by a GitRepository
|
|
939
1054
|
register_datasource_contents(
|
|
940
1055
|
[
|
|
@@ -978,5 +1093,15 @@ register_datasource_contents(
|
|
|
978
1093
|
callback=refresh_git_export_templates,
|
|
979
1094
|
),
|
|
980
1095
|
),
|
|
1096
|
+
(
|
|
1097
|
+
"extras.gitrepository",
|
|
1098
|
+
DatasourceContent(
|
|
1099
|
+
name="graphql queries",
|
|
1100
|
+
content_identifier="extras.graphqlquery",
|
|
1101
|
+
icon="mdi-graphql",
|
|
1102
|
+
weight=400,
|
|
1103
|
+
callback=refresh_git_graphql_queries,
|
|
1104
|
+
),
|
|
1105
|
+
),
|
|
981
1106
|
]
|
|
982
1107
|
)
|