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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Generated by Django 4.2.17 on 2025-01-08 16:59
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
import nautobot.extras.utils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Migration(migrations.Migration):
|
|
10
|
+
dependencies = [
|
|
11
|
+
("contenttypes", "0002_remove_content_type_name"),
|
|
12
|
+
("extras", "0121_alter_team_contacts"),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.AddField(
|
|
17
|
+
model_name="graphqlquery",
|
|
18
|
+
name="owner_content_type",
|
|
19
|
+
field=models.ForeignKey(
|
|
20
|
+
blank=True,
|
|
21
|
+
default=None,
|
|
22
|
+
limit_choices_to=nautobot.extras.utils.FeatureQuery("graphql_query_owners"),
|
|
23
|
+
null=True,
|
|
24
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
25
|
+
related_name="graphql_queries",
|
|
26
|
+
to="contenttypes.contenttype",
|
|
27
|
+
),
|
|
28
|
+
),
|
|
29
|
+
migrations.AddField(
|
|
30
|
+
model_name="graphqlquery",
|
|
31
|
+
name="owner_object_id",
|
|
32
|
+
field=models.UUIDField(blank=True, default=None, null=True),
|
|
33
|
+
),
|
|
34
|
+
]
|
|
@@ -370,11 +370,11 @@ class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
|
370
370
|
|
|
371
371
|
def get_for_model(self, model, exclude_filter_disabled=False):
|
|
372
372
|
"""
|
|
373
|
-
Return all CustomFields assigned to the given model.
|
|
373
|
+
Return (and cache) all CustomFields assigned to the given model.
|
|
374
374
|
|
|
375
375
|
Args:
|
|
376
|
-
model: The django model to which custom fields are registered
|
|
377
|
-
exclude_filter_disabled: Exclude any custom fields which have filter logic disabled
|
|
376
|
+
model (Model): The django model to which custom fields are registered
|
|
377
|
+
exclude_filter_disabled (bool): Exclude any custom fields which have filter logic disabled
|
|
378
378
|
"""
|
|
379
379
|
concrete_model = model._meta.concrete_model
|
|
380
380
|
cache_key = (
|
|
@@ -521,6 +521,26 @@ class CustomField(
|
|
|
521
521
|
def __str__(self):
|
|
522
522
|
return self.label
|
|
523
523
|
|
|
524
|
+
@property
|
|
525
|
+
def choices_cache_key(self):
|
|
526
|
+
return f"nautobot.extras.customfield.choices.{self.pk}"
|
|
527
|
+
|
|
528
|
+
@property
|
|
529
|
+
def choices(self) -> list[str]:
|
|
530
|
+
"""
|
|
531
|
+
Cacheable shorthand for retrieving custom_field_choices values associated with this model.
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
list[str]: List of choice values, ordered by weight.
|
|
535
|
+
"""
|
|
536
|
+
if self.type not in [CustomFieldTypeChoices.TYPE_SELECT, CustomFieldTypeChoices.TYPE_MULTISELECT]:
|
|
537
|
+
return []
|
|
538
|
+
choices = cache.get(self.choices_cache_key)
|
|
539
|
+
if choices is None:
|
|
540
|
+
choices = list(self.custom_field_choices.order_by("weight", "value").values_list("value", flat=True))
|
|
541
|
+
cache.set(self.choices_cache_key, choices)
|
|
542
|
+
return choices
|
|
543
|
+
|
|
524
544
|
def save(self, *args, **kwargs):
|
|
525
545
|
self.clean()
|
|
526
546
|
super().save(*args, **kwargs)
|
|
@@ -679,12 +699,11 @@ class CustomField(
|
|
|
679
699
|
|
|
680
700
|
# Select or Multi-select
|
|
681
701
|
else:
|
|
682
|
-
choices = [(
|
|
683
|
-
default_choice = self.custom_field_choices.filter(value=self.default).first()
|
|
702
|
+
choices = [(value, value) for value in self.choices]
|
|
684
703
|
|
|
685
704
|
# Set the initial value to the first available choice (if any)
|
|
686
705
|
if self.type == CustomFieldTypeChoices.TYPE_SELECT and not for_filter_form:
|
|
687
|
-
if not required or
|
|
706
|
+
if not required or self.default not in self.choices:
|
|
688
707
|
choices = add_blank_choice(choices)
|
|
689
708
|
field_class = CSVChoiceField if for_csv_import else forms.ChoiceField
|
|
690
709
|
field = field_class(
|
|
@@ -779,17 +798,15 @@ class CustomField(
|
|
|
779
798
|
|
|
780
799
|
# Validate selected choice
|
|
781
800
|
elif self.type == CustomFieldTypeChoices.TYPE_SELECT:
|
|
782
|
-
if value not in self.
|
|
783
|
-
raise ValidationError(
|
|
784
|
-
f"Invalid choice ({value}). Available choices are: {', '.join(self.custom_field_choices.values_list('value', flat=True))}"
|
|
785
|
-
)
|
|
801
|
+
if value not in self.choices:
|
|
802
|
+
raise ValidationError(f"Invalid choice ({value}). Available choices are: {', '.join(self.choices)}")
|
|
786
803
|
|
|
787
804
|
elif self.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
|
|
788
805
|
if isinstance(value, str):
|
|
789
806
|
value = value.split(",")
|
|
790
|
-
if not set(value).issubset(self.
|
|
807
|
+
if not set(value).issubset(self.choices):
|
|
791
808
|
raise ValidationError(
|
|
792
|
-
f"Invalid choice(s) ({value}). Available choices are: {', '.join(self.
|
|
809
|
+
f"Invalid choice(s) ({value}). Available choices are: {', '.join(self.choices)}"
|
|
793
810
|
)
|
|
794
811
|
|
|
795
812
|
elif self.required:
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"""Models for representing external data sources."""
|
|
2
2
|
|
|
3
|
+
from contextlib import contextmanager
|
|
3
4
|
from importlib.util import find_spec
|
|
5
|
+
import logging
|
|
4
6
|
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import tempfile
|
|
5
9
|
|
|
6
10
|
from django.conf import settings
|
|
7
11
|
from django.core.exceptions import ValidationError
|
|
@@ -12,12 +16,16 @@ from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
|
12
16
|
from nautobot.core.models.fields import AutoSlugField, LaxURLField, slugify_dashes_to_underscores
|
|
13
17
|
from nautobot.core.models.generics import PrimaryModel
|
|
14
18
|
from nautobot.core.models.validators import EnhancedURLValidator
|
|
19
|
+
from nautobot.core.utils.git import GitRepo
|
|
15
20
|
from nautobot.extras.utils import check_if_key_is_graphql_safe, extras_features
|
|
16
21
|
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
@extras_features(
|
|
19
26
|
"config_context_owners",
|
|
20
27
|
"export_template_owners",
|
|
28
|
+
"graphql_query_owners",
|
|
21
29
|
"graphql",
|
|
22
30
|
"job_results",
|
|
23
31
|
"webhooks",
|
|
@@ -167,3 +175,80 @@ class GitRepository(PrimaryModel):
|
|
|
167
175
|
if dry_run:
|
|
168
176
|
return enqueue_git_repository_diff_origin_and_local(self, user)
|
|
169
177
|
return enqueue_pull_git_repository_and_refresh_data(self, user)
|
|
178
|
+
|
|
179
|
+
@contextmanager
|
|
180
|
+
def clone_to_directory_context(self, path=None, branch=None, head=None, depth=0):
|
|
181
|
+
"""
|
|
182
|
+
Context manager to perform a (shallow or full) clone of the Git repository in a temporary directory.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
path (str, optional): The absolute directory path to clone into. If not specified, `tempfile.gettempdir()` will be used.
|
|
186
|
+
branch (str, optional): The branch to checkout. If not set, the GitRepository.branch will be used.
|
|
187
|
+
head (str, optional): Git commit hash to check out instead of pulling branch latest.
|
|
188
|
+
depth (int, optional): The depth of the clone. If set to 0, a full clone will be performed.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Returns the absolute path of the cloned repo if clone was successful, otherwise returns None.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
if branch and head:
|
|
195
|
+
raise ValueError("Cannot specify both branch and head")
|
|
196
|
+
|
|
197
|
+
path_name = None
|
|
198
|
+
try:
|
|
199
|
+
path_name = self.clone_to_directory(path=path, branch=branch, head=head, depth=depth)
|
|
200
|
+
yield path_name
|
|
201
|
+
finally:
|
|
202
|
+
# Cleanup the temporary directory
|
|
203
|
+
if path_name:
|
|
204
|
+
self.cleanup_cloned_directory(path_name)
|
|
205
|
+
|
|
206
|
+
def clone_to_directory(self, path=None, branch=None, head=None, depth=0):
|
|
207
|
+
"""
|
|
208
|
+
Perform a (shallow or full) clone of the Git repository in a temporary directory.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
path (str, optional): The absolute directory path to clone into. If not specified, `tempfile.gettempdir()` will be used.
|
|
212
|
+
branch (str, optional): The branch to checkout. If not set, the GitRepository.branch will be used.
|
|
213
|
+
head (str, optional): Git commit hash to check out instead of pulling branch latest.
|
|
214
|
+
depth (int, optional): The depth of the clone. If set to 0, a full clone will be performed.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Returns the absolute path of the cloned repo if clone was successful, otherwise returns None.
|
|
218
|
+
"""
|
|
219
|
+
if branch and head:
|
|
220
|
+
raise ValueError("Cannot specify both branch and head")
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
path_name = tempfile.mkdtemp(dir=path, prefix=self.slug)
|
|
224
|
+
except PermissionError as e:
|
|
225
|
+
logger.error(f"Failed to create temporary directory at {path}: {e}")
|
|
226
|
+
raise e
|
|
227
|
+
|
|
228
|
+
if not branch:
|
|
229
|
+
branch = self.branch
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
repo_helper = GitRepo(path_name, self.remote_url, depth=depth, branch=branch)
|
|
233
|
+
if head:
|
|
234
|
+
repo_helper.checkout(branch, head)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
logger.error(f"Failed to clone repository {self.name} to {path_name}: {e}")
|
|
237
|
+
raise e
|
|
238
|
+
|
|
239
|
+
logger.info(f"Cloned repository {self.name} to {path_name}")
|
|
240
|
+
return path_name
|
|
241
|
+
|
|
242
|
+
def cleanup_cloned_directory(self, path):
|
|
243
|
+
"""
|
|
244
|
+
Cleanup the cloned directory.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
path (str): The absolute directory path to cleanup.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
shutil.rmtree(path)
|
|
252
|
+
except OSError as os_error:
|
|
253
|
+
# log error if the cleanup fails
|
|
254
|
+
logger.error(f"Failed to cleanup temporary directory at {path}: {os_error}")
|
nautobot/extras/models/models.py
CHANGED
|
@@ -680,6 +680,21 @@ class GraphQLQuery(
|
|
|
680
680
|
SavedViewMixin,
|
|
681
681
|
BaseModel,
|
|
682
682
|
):
|
|
683
|
+
# A Graphql Query *may* be owned by another model, such as a GitRepository, or it may be un-owned
|
|
684
|
+
owner_content_type = models.ForeignKey(
|
|
685
|
+
to=ContentType,
|
|
686
|
+
on_delete=models.CASCADE,
|
|
687
|
+
limit_choices_to=FeatureQuery("graphql_query_owners"),
|
|
688
|
+
default=None,
|
|
689
|
+
null=True,
|
|
690
|
+
blank=True,
|
|
691
|
+
related_name="graphql_queries",
|
|
692
|
+
)
|
|
693
|
+
owner_object_id = models.UUIDField(default=None, null=True, blank=True)
|
|
694
|
+
owner = GenericForeignKey(
|
|
695
|
+
ct_field="owner_content_type",
|
|
696
|
+
fk_field="owner_object_id",
|
|
697
|
+
)
|
|
683
698
|
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
|
|
684
699
|
query = models.TextField()
|
|
685
700
|
variables = models.JSONField(encoder=DjangoJSONEncoder, default=dict, blank=True)
|
|
@@ -261,15 +261,27 @@ class RelationshipModel(models.Model):
|
|
|
261
261
|
remote_model = remote_ct.model_class()
|
|
262
262
|
if remote_model is not None:
|
|
263
263
|
if not relationship.symmetric:
|
|
264
|
-
query_params = {
|
|
265
|
-
|
|
264
|
+
query_params = {
|
|
265
|
+
f"{peer_side}_for_associations__relationship": relationship,
|
|
266
|
+
f"{peer_side}_for_associations__{side}_id": self.pk,
|
|
267
|
+
}
|
|
268
|
+
# Get the related objects for this relationship on the opposite side.
|
|
269
|
+
resp[side][relationship] = remote_model.objects.filter(**query_params).distinct()
|
|
266
270
|
if not relationship.has_many(peer_side):
|
|
267
271
|
resp[side][relationship] = resp[side][relationship].first()
|
|
268
272
|
else:
|
|
273
|
+
side_query_params = {
|
|
274
|
+
f"{peer_side}_for_associations__relationship": relationship,
|
|
275
|
+
f"{peer_side}_for_associations__{side}_id": self.pk,
|
|
276
|
+
}
|
|
277
|
+
peer_side_query_params = {
|
|
278
|
+
f"{side}_for_associations__relationship": relationship,
|
|
279
|
+
f"{side}_for_associations__{peer_side}_id": self.pk,
|
|
280
|
+
}
|
|
281
|
+
# Get the related objects based on the pks we gathered.
|
|
269
282
|
resp[RelationshipSideChoices.SIDE_PEER][relationship] = remote_model.objects.filter(
|
|
270
|
-
Q(
|
|
271
|
-
|
|
272
|
-
).exclude(pk=self.pk)
|
|
283
|
+
Q(**side_query_params) | Q(**peer_side_query_params)
|
|
284
|
+
).distinct()
|
|
273
285
|
if not relationship.has_many(peer_side):
|
|
274
286
|
resp[side][relationship] = resp[side][relationship].first()
|
|
275
287
|
else:
|
nautobot/extras/signals.py
CHANGED
|
@@ -28,6 +28,7 @@ from nautobot.extras.models import (
|
|
|
28
28
|
ComputedField,
|
|
29
29
|
ContactAssociation,
|
|
30
30
|
CustomField,
|
|
31
|
+
CustomFieldChoice,
|
|
31
32
|
DynamicGroup,
|
|
32
33
|
DynamicGroupMembership,
|
|
33
34
|
GitRepository,
|
|
@@ -97,6 +98,19 @@ def invalidate_models_cache(sender, **kwargs):
|
|
|
97
98
|
cache.delete_pattern(f"{manager.keys_for_model.cache_key_prefix}.*")
|
|
98
99
|
|
|
99
100
|
|
|
101
|
+
@receiver(post_delete, sender=CustomField)
|
|
102
|
+
@receiver(post_delete, sender=CustomFieldChoice)
|
|
103
|
+
@receiver(post_save, sender=CustomFieldChoice)
|
|
104
|
+
@receiver(post_save, sender=CustomField)
|
|
105
|
+
def invalidate_choices_cache(sender, instance, **kwargs):
|
|
106
|
+
"""Invalidate the choices cache for CustomFields."""
|
|
107
|
+
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
108
|
+
if sender is CustomField:
|
|
109
|
+
cache.delete(instance.choices_cache_key)
|
|
110
|
+
else:
|
|
111
|
+
cache.delete(instance.custom_field.choices_cache_key)
|
|
112
|
+
|
|
113
|
+
|
|
100
114
|
@receiver(post_save, sender=Relationship)
|
|
101
115
|
@receiver(m2m_changed, sender=Relationship)
|
|
102
116
|
@receiver(post_delete, sender=Relationship)
|
|
@@ -376,7 +390,7 @@ def git_repository_pre_delete(instance, **kwargs):
|
|
|
376
390
|
app.control.broadcast("discard_git_repository", repository_slug=instance.slug)
|
|
377
391
|
# But we don't have an equivalent way to broadcast to any other Django instances.
|
|
378
392
|
# For now we just delete the one that we have locally and rely on other methods,
|
|
379
|
-
# such as the import_jobs() signal that runs on
|
|
393
|
+
# such as the import_jobs() signal that runs on post migrate,
|
|
380
394
|
# to clean up other clones as they're encountered.
|
|
381
395
|
if os.path.isdir(instance.filesystem_path):
|
|
382
396
|
shutil.rmtree(instance.filesystem_path)
|
|
@@ -1,63 +1 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
2
|
-
{% load buttons %}
|
|
3
|
-
{% load plugins %}
|
|
4
|
-
{% load perms %}
|
|
5
|
-
{% load helpers %}
|
|
6
|
-
|
|
7
|
-
{% block content_left_page %}
|
|
8
|
-
<div class="panel panel-default">
|
|
9
|
-
<div class="panel-heading">
|
|
10
|
-
<strong>Relationship</strong>
|
|
11
|
-
</div>
|
|
12
|
-
<table class="table table-hover panel-body attr-table">
|
|
13
|
-
<tr>
|
|
14
|
-
<td>Description</td>
|
|
15
|
-
<td>{{ object.description | placeholder }}</td>
|
|
16
|
-
</tr>
|
|
17
|
-
<tr>
|
|
18
|
-
<td>Type</td>
|
|
19
|
-
<td>{{ object.type }}</td>
|
|
20
|
-
</tr>
|
|
21
|
-
<tr>
|
|
22
|
-
<td>Required On</td>
|
|
23
|
-
<td>{{ object.get_required_on_display }}</td>
|
|
24
|
-
</tr>
|
|
25
|
-
<tr>
|
|
26
|
-
<td>On Advanced Tab</td>
|
|
27
|
-
<td>{{ object.advanced_ui | render_boolean }}</td>
|
|
28
|
-
</tr>
|
|
29
|
-
<tr>
|
|
30
|
-
<td>Source Content Type</td>
|
|
31
|
-
<td>{{ object.source_type }}</td>
|
|
32
|
-
</tr>
|
|
33
|
-
<tr>
|
|
34
|
-
<td>Source Label</td>
|
|
35
|
-
<td>{{ object.source_label | placeholder }}</td>
|
|
36
|
-
</tr>
|
|
37
|
-
<tr>
|
|
38
|
-
<td>Source Filter</td>
|
|
39
|
-
<td>{% if object.source_filter %}<pre>{{ object.source_filter | render_json }}</pre>{% else %}{{ None | placeholder }}{% endif %}</td>
|
|
40
|
-
</tr>
|
|
41
|
-
<tr>
|
|
42
|
-
<td>Hide on Source Object</td>
|
|
43
|
-
<td>{{ object.source_hidden | render_boolean }}</td>
|
|
44
|
-
</tr>
|
|
45
|
-
<tr>
|
|
46
|
-
<td>Destination Content Type</td>
|
|
47
|
-
<td>{{ object.destination_type }}</td>
|
|
48
|
-
</tr>
|
|
49
|
-
<tr>
|
|
50
|
-
<td>Destination Label</td>
|
|
51
|
-
<td>{{ object.destination_label | placeholder }}</td>
|
|
52
|
-
</tr>
|
|
53
|
-
<tr>
|
|
54
|
-
<td>Destination Filter</td>
|
|
55
|
-
<td>{% if object.destination_filter %}<pre>{{ object.destination_filter | render_json }}</pre>{% else %}{{ None | placeholder }}{% endif %}</td>
|
|
56
|
-
</tr>
|
|
57
|
-
<tr>
|
|
58
|
-
<td>Hide on Destination Object</td>
|
|
59
|
-
<td>{{ object.destination_hidden | render_boolean }}</td>
|
|
60
|
-
</tr>
|
|
61
|
-
</table>
|
|
62
|
-
</div>
|
|
63
|
-
{% endblock content_left_page %}
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{% extends 'generic/
|
|
1
|
+
{% extends 'generic/object_retrieve.html' %}
|
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
SOURCE_DIR = os.path.join(os.path.dirname(__file__), "git_data")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def create_and_populate_git_repository(target_path):
|
|
17
|
+
def create_and_populate_git_repository(target_path, divergent_branch=None):
|
|
18
18
|
"""
|
|
19
19
|
Create a Git repository in `target_path` and populate it with commits and tags based on contents of `SOURCE_DIR`.
|
|
20
20
|
|
|
@@ -40,6 +40,9 @@ def create_and_populate_git_repository(target_path):
|
|
|
40
40
|
Note that each commit is fully defined by the files in the appropriate subdirectory; if you want a file to exist
|
|
41
41
|
across multiple separate commits, it must exist in multiple subdirectories. Use of symlinks is encouraged in such
|
|
42
42
|
a scenario.
|
|
43
|
+
|
|
44
|
+
You can optionally create and check out a divergent branch from the main branch by passing a branch name as the `divergent_branch`.
|
|
45
|
+
This will write a commit to the divergent branch and tag it with the branch name with the `-tag` suffix.
|
|
43
46
|
"""
|
|
44
47
|
os.makedirs(target_path, exist_ok=True)
|
|
45
48
|
repo = Repo.init(target_path, initial_branch="main")
|
|
@@ -69,6 +72,11 @@ def create_and_populate_git_repository(target_path):
|
|
|
69
72
|
# Directory "01-valid-files" --> tag "valid-files" so that we won't break tests if we renumber the directories
|
|
70
73
|
repo.create_tag(dirname[3:], message=f"Tag based on {dirname} files")
|
|
71
74
|
|
|
75
|
+
if divergent_branch:
|
|
76
|
+
repo.create_head(divergent_branch)
|
|
77
|
+
repo.index.commit("divergent-branch")
|
|
78
|
+
repo.create_tag(f"{divergent_branch}-tag", message=f"Tag for divergent branch {divergent_branch}")
|
|
79
|
+
|
|
72
80
|
|
|
73
81
|
if __name__ == "__main__":
|
|
74
82
|
directory_path = tempfile.TemporaryDirectory().name # pylint: disable=consider-using-with
|
|
@@ -6,28 +6,41 @@ from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Man
|
|
|
6
6
|
from nautobot.extras.models import Role, Status
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def create_test_device():
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
def create_test_device(name=None, location_name=None, test_uuid=None):
|
|
10
|
+
if not test_uuid:
|
|
11
|
+
test_uuid = str(uuid.uuid4())
|
|
12
|
+
if not name:
|
|
13
|
+
name = f"Test Device {test_uuid}"
|
|
14
|
+
if not location_name:
|
|
15
|
+
location_name = f"Test Location {test_uuid}"
|
|
16
|
+
|
|
17
|
+
location_type, location_type_created = LocationType.objects.get_or_create(name=f"Test Location Type {test_uuid}")
|
|
18
|
+
if location_type_created:
|
|
19
|
+
location_type.content_types.add(ContentType.objects.get_for_model(Device))
|
|
20
|
+
location_type.save()
|
|
21
|
+
|
|
20
22
|
location_status = Status.objects.get_for_model(Location).first()
|
|
21
|
-
location = Location.objects.
|
|
22
|
-
name=
|
|
23
|
+
location, _ = Location.objects.get_or_create(
|
|
24
|
+
name=location_name,
|
|
23
25
|
status=location_status,
|
|
24
26
|
location_type=location_type,
|
|
25
27
|
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
device_role, device_role_created = Role.objects.get_or_create(name="Device Role")
|
|
30
|
+
if device_role_created:
|
|
31
|
+
device_role.content_types.add(ContentType.objects.get_for_model(Device))
|
|
32
|
+
device_role.save()
|
|
33
|
+
|
|
34
|
+
manufacturer, _ = Manufacturer.objects.get_or_create(
|
|
35
|
+
name=f"Test Manufacturer {test_uuid}",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
device_type, _ = DeviceType.objects.get_or_create(manufacturer=manufacturer, model=f"Test Model {test_uuid}")
|
|
39
|
+
|
|
40
|
+
return Device.objects.create(
|
|
41
|
+
name=name,
|
|
28
42
|
role=device_role,
|
|
29
43
|
device_type=device_type,
|
|
30
44
|
location=location,
|
|
31
45
|
status=location_status,
|
|
32
46
|
)
|
|
33
|
-
return device
|