nautobot 2.3.8__py3-none-any.whl → 2.3.9__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/apps/tables.py +2 -0
- nautobot/core/forms/__init__.py +4 -0
- nautobot/core/forms/fields.py +32 -0
- nautobot/core/jobs/__init__.py +24 -8
- nautobot/core/models/tree_queries.py +8 -0
- nautobot/core/settings.py +7 -0
- nautobot/core/settings.yaml +10 -0
- nautobot/core/signals.py +5 -4
- nautobot/core/templates/nautobot_config.py.j2 +4 -0
- nautobot/dcim/forms.py +30 -27
- nautobot/dcim/models/device_components.py +5 -0
- nautobot/dcim/tables/devices.py +4 -2
- nautobot/dcim/tests/test_models.py +16 -0
- nautobot/extras/context_managers.py +7 -8
- nautobot/extras/datasources/__init__.py +2 -0
- nautobot/extras/datasources/git.py +30 -49
- nautobot/extras/datasources/registry.py +2 -2
- nautobot/extras/jobs.py +17 -5
- nautobot/extras/models/datasources.py +6 -0
- nautobot/extras/models/groups.py +47 -33
- nautobot/extras/models/jobs.py +1 -1
- nautobot/extras/plugins/__init__.py +165 -0
- nautobot/extras/templates/extras/plugin_detail.html +33 -0
- nautobot/extras/tests/test_context_managers.py +16 -7
- nautobot/extras/tests/test_datasources.py +88 -1
- nautobot/extras/tests/test_dynamicgroups.py +12 -0
- nautobot/extras/tests/test_plugins.py +94 -0
- nautobot/extras/views.py +3 -1
- nautobot/project-static/docs/404.html +24 -3
- nautobot/project-static/docs/apps/index.html +24 -3
- nautobot/project-static/docs/apps/nautobot-apps.html +24 -3
- nautobot/project-static/docs/assets/javascripts/{bundle.525ec568.min.js → bundle.83f73b43.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.525ec568.min.js.map → bundle.83f73b43.min.js.map} +3 -3
- nautobot/project-static/docs/assets/stylesheets/main.0253249f.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.0253249f.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +49 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +138 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +24 -3
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +24 -3
- nautobot/project-static/docs/development/apps/api/configuration-view.html +24 -3
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +24 -3
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +24 -3
- nautobot/project-static/docs/development/apps/api/models/global-search.html +24 -3
- nautobot/project-static/docs/development/apps/api/models/graphql.html +24 -3
- nautobot/project-static/docs/development/apps/api/models/index.html +24 -3
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +24 -3
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +27 -6
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +8823 -0
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +27 -6
- nautobot/project-static/docs/development/apps/api/prometheus.html +24 -3
- nautobot/project-static/docs/development/apps/api/setup.html +33 -11
- nautobot/project-static/docs/development/apps/api/testing.html +24 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +24 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +24 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +24 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +24 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/base-template.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/index.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/notes.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +24 -3
- nautobot/project-static/docs/development/apps/api/views/urls.html +24 -3
- nautobot/project-static/docs/development/apps/index.html +24 -3
- nautobot/project-static/docs/development/apps/migration/code-updates.html +24 -3
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +24 -3
- nautobot/project-static/docs/development/apps/migration/from-v1.html +24 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +24 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +24 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +24 -3
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +24 -3
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +24 -3
- nautobot/project-static/docs/development/core/application-registry.html +24 -3
- nautobot/project-static/docs/development/core/best-practices.html +24 -3
- nautobot/project-static/docs/development/core/bootstrap-ui.html +24 -3
- nautobot/project-static/docs/development/core/caching.html +24 -3
- nautobot/project-static/docs/development/core/controllers.html +24 -3
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +24 -3
- nautobot/project-static/docs/development/core/generic-views.html +24 -3
- nautobot/project-static/docs/development/core/getting-started.html +24 -3
- nautobot/project-static/docs/development/core/homepage.html +24 -3
- nautobot/project-static/docs/development/core/index.html +24 -3
- nautobot/project-static/docs/development/core/model-checklist.html +24 -3
- nautobot/project-static/docs/development/core/model-features.html +24 -3
- nautobot/project-static/docs/development/core/natural-keys.html +24 -3
- nautobot/project-static/docs/development/core/navigation-menu.html +24 -3
- nautobot/project-static/docs/development/core/release-checklist.html +24 -3
- nautobot/project-static/docs/development/core/role-internals.html +24 -3
- nautobot/project-static/docs/development/core/settings.html +24 -3
- nautobot/project-static/docs/development/core/style-guide.html +24 -3
- nautobot/project-static/docs/development/core/templates.html +24 -3
- nautobot/project-static/docs/development/core/testing.html +24 -3
- nautobot/project-static/docs/development/core/user-preferences.html +24 -3
- nautobot/project-static/docs/development/index.html +24 -3
- nautobot/project-static/docs/development/jobs/index.html +24 -3
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +24 -3
- nautobot/project-static/docs/index.html +24 -3
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +24 -3
- nautobot/project-static/docs/overview/design_philosophy.html +24 -3
- nautobot/project-static/docs/release-notes/index.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.0.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.1.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.2.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.3.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.4.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.5.html +24 -3
- nautobot/project-static/docs/release-notes/version-1.6.html +24 -3
- nautobot/project-static/docs/release-notes/version-2.0.html +24 -3
- nautobot/project-static/docs/release-notes/version-2.1.html +24 -3
- nautobot/project-static/docs/release-notes/version-2.2.html +24 -3
- nautobot/project-static/docs/release-notes/version-2.3.html +285 -114
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +273 -269
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +24 -3
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +24 -3
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +24 -3
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +24 -3
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +24 -3
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +29 -4
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +24 -3
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/index.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +24 -3
- nautobot/project-static/docs/user-guide/administration/installation/services.html +24 -3
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +24 -3
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +24 -3
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +24 -3
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +24 -3
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +24 -3
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +24 -3
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +24 -3
- nautobot/project-static/docs/user-guide/index.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +24 -3
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +24 -3
- nautobot/project-static/js/forms.js +41 -5
- nautobot/virtualization/tables.py +1 -1
- {nautobot-2.3.8.dist-info → nautobot-2.3.9.dist-info}/METADATA +2 -2
- {nautobot-2.3.8.dist-info → nautobot-2.3.9.dist-info}/RECORD +316 -315
- nautobot/project-static/docs/assets/stylesheets/main.8c3ca2c6.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.8c3ca2c6.min.css.map +0 -1
- {nautobot-2.3.8.dist-info → nautobot-2.3.9.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.8.dist-info → nautobot-2.3.9.dist-info}/NOTICE +0 -0
- {nautobot-2.3.8.dist-info → nautobot-2.3.9.dist-info}/WHEEL +0 -0
- {nautobot-2.3.8.dist-info → nautobot-2.3.9.dist-info}/entry_points.txt +0 -0
nautobot/apps/tables.py
CHANGED
|
@@ -15,6 +15,7 @@ from nautobot.core.tables import (
|
|
|
15
15
|
TagColumn,
|
|
16
16
|
ToggleColumn,
|
|
17
17
|
)
|
|
18
|
+
from nautobot.extras.plugins import TableExtension
|
|
18
19
|
from nautobot.extras.tables import RoleTableMixin, StatusTableMixin
|
|
19
20
|
|
|
20
21
|
__all__ = (
|
|
@@ -32,5 +33,6 @@ __all__ = (
|
|
|
32
33
|
"RoleTableMixin",
|
|
33
34
|
"StatusTableMixin",
|
|
34
35
|
"TagColumn",
|
|
36
|
+
"TableExtension",
|
|
35
37
|
"ToggleColumn",
|
|
36
38
|
)
|
nautobot/core/forms/__init__.py
CHANGED
|
@@ -7,6 +7,8 @@ from nautobot.core.forms.constants import (
|
|
|
7
7
|
NUMERIC_EXPANSION_PATTERN,
|
|
8
8
|
)
|
|
9
9
|
from nautobot.core.forms.fields import (
|
|
10
|
+
AutoPositionField,
|
|
11
|
+
AutoPositionPatternField,
|
|
10
12
|
CommentField,
|
|
11
13
|
CSVChoiceField,
|
|
12
14
|
CSVContentTypeField,
|
|
@@ -80,6 +82,8 @@ __all__ = (
|
|
|
80
82
|
"ALPHANUMERIC_EXPANSION_PATTERN",
|
|
81
83
|
"APISelect",
|
|
82
84
|
"APISelectMultiple",
|
|
85
|
+
"AutoPositionField",
|
|
86
|
+
"AutoPositionPatternField",
|
|
83
87
|
"BOOLEAN_CHOICES",
|
|
84
88
|
"BOOLEAN_WITH_BLANK_CHOICES",
|
|
85
89
|
"BootstrapMixin",
|
nautobot/core/forms/fields.py
CHANGED
|
@@ -444,6 +444,38 @@ class SlugField(django_forms.SlugField):
|
|
|
444
444
|
self.widget.attrs["slug-source"] = slug_source
|
|
445
445
|
|
|
446
446
|
|
|
447
|
+
class AutoPositionField(django_forms.CharField):
|
|
448
|
+
def __init__(self, source="name", *args, **kwargs):
|
|
449
|
+
"""
|
|
450
|
+
Instantiate a AutoPositionField.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
source (str, tuple): Name of the field (or a list of field names) that will be used to suggest a position.
|
|
454
|
+
"""
|
|
455
|
+
kwargs.setdefault("label", "Position")
|
|
456
|
+
kwargs.setdefault("widget", forms.SlugWidget)
|
|
457
|
+
super().__init__(*args, **kwargs)
|
|
458
|
+
if isinstance(source, (tuple, list)):
|
|
459
|
+
source = " ".join(source)
|
|
460
|
+
self.widget.attrs["source"] = source
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class AutoPositionPatternField(ExpandableNameField):
|
|
464
|
+
def __init__(self, source="name_pattern", *args, **kwargs):
|
|
465
|
+
"""
|
|
466
|
+
Instantiate a AutoPositionPatternField.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
source (str, tuple): Name pattern of the field (or a list of field names) that will be used to suggest a position pattern.
|
|
470
|
+
"""
|
|
471
|
+
kwargs.setdefault("label", "Position")
|
|
472
|
+
kwargs.setdefault("widget", forms.SlugWidget)
|
|
473
|
+
super().__init__(*args, **kwargs)
|
|
474
|
+
if isinstance(source, (tuple, list)):
|
|
475
|
+
source = " ".join(source)
|
|
476
|
+
self.widget.attrs["source"] = source
|
|
477
|
+
|
|
478
|
+
|
|
447
479
|
class DynamicModelChoiceMixin:
|
|
448
480
|
"""
|
|
449
481
|
:param display_field: The name of the attribute of an API response object to display in the selection list
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -19,7 +19,12 @@ from nautobot.core.jobs.cleanup import LogsCleanup
|
|
|
19
19
|
from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
|
|
20
20
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
21
21
|
from nautobot.core.utils.requests import get_filterable_params_from_filter_params
|
|
22
|
-
from nautobot.extras.datasources import
|
|
22
|
+
from nautobot.extras.datasources import (
|
|
23
|
+
ensure_git_repository,
|
|
24
|
+
git_repository_dry_run,
|
|
25
|
+
refresh_datasource_content,
|
|
26
|
+
refresh_job_code_from_repository,
|
|
27
|
+
)
|
|
23
28
|
from nautobot.extras.jobs import BooleanVar, ChoiceVar, FileVar, Job, ObjectVar, RunJobTaskFailed, StringVar, TextVar
|
|
24
29
|
from nautobot.extras.models import ExportTemplate, GitRepository
|
|
25
30
|
|
|
@@ -49,13 +54,24 @@ class GitRepositorySync(Job):
|
|
|
49
54
|
self.logger.info(f'Creating/refreshing local copy of Git repository "{repository.name}"...')
|
|
50
55
|
|
|
51
56
|
try:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
with transaction.atomic():
|
|
58
|
+
ensure_git_repository(repository, logger=self.logger)
|
|
59
|
+
refresh_datasource_content("extras.gitrepository", repository, user, job_result, delete=False)
|
|
60
|
+
# Given that the above succeeded, tell all workers (including ourself) to call ensure_git_repository()
|
|
61
|
+
app.control.broadcast(
|
|
62
|
+
"refresh_git_repository", repository_pk=repository.pk, head=repository.current_head
|
|
63
|
+
)
|
|
64
|
+
if job_result.duration:
|
|
65
|
+
self.logger.info("Repository synchronization completed in %s", job_result.duration)
|
|
66
|
+
except Exception:
|
|
67
|
+
job_result.log("Changes to database records have been reverted.")
|
|
68
|
+
# Re-check-out previous commit if any
|
|
69
|
+
repository.refresh_from_db()
|
|
70
|
+
if repository.current_head:
|
|
71
|
+
job_result.log(f"Attempting to revert local repository clone to commit {repository.current_head}")
|
|
72
|
+
ensure_git_repository(repository, logger=self.logger, head=repository.current_head)
|
|
73
|
+
refresh_job_code_from_repository(repository.slug, ignore_import_errors=True)
|
|
74
|
+
raise
|
|
59
75
|
|
|
60
76
|
|
|
61
77
|
class GitRepositoryDryRun(Job):
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from django.core.cache import cache
|
|
2
2
|
from django.db.models import Case, When
|
|
3
|
+
from django.db.models.signals import post_delete, post_save
|
|
3
4
|
from tree_queries.models import TreeNode
|
|
4
5
|
from tree_queries.query import TreeManager as TreeManager_, TreeQuerySet as TreeQuerySet_
|
|
5
6
|
|
|
6
7
|
from nautobot.core.models import BaseManager, querysets
|
|
8
|
+
from nautobot.core.signals import invalidate_max_depth_cache
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
|
|
@@ -117,3 +119,9 @@ class TreeModel(TreeNode):
|
|
|
117
119
|
display_str += self.name
|
|
118
120
|
cache.set(cache_key, display_str, 5)
|
|
119
121
|
return display_str
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def __init_subclass__(cls, **kwargs):
|
|
125
|
+
super().__init_subclass__(**kwargs)
|
|
126
|
+
post_save.connect(invalidate_max_depth_cache, sender=cls)
|
|
127
|
+
post_delete.connect(invalidate_max_depth_cache, sender=cls)
|
nautobot/core/settings.py
CHANGED
|
@@ -418,7 +418,14 @@ if "NAUTOBOT_ALLOWED_HOSTS" in os.environ and os.environ["NAUTOBOT_ALLOWED_HOSTS
|
|
|
418
418
|
ALLOWED_HOSTS = os.environ["NAUTOBOT_ALLOWED_HOSTS"].split(" ")
|
|
419
419
|
else:
|
|
420
420
|
ALLOWED_HOSTS = []
|
|
421
|
+
|
|
422
|
+
# Allow CSRF trusted origins to be set via an environment variable
|
|
423
|
+
# This allows Nautobot to be run behind a reverse proxy that terminates TLS
|
|
424
|
+
# and fix potential issues with CSRF validation
|
|
421
425
|
CSRF_TRUSTED_ORIGINS = []
|
|
426
|
+
if "NAUTOBOT_CSRF_TRUSTED_ORIGINS" in os.environ and os.environ["NAUTOBOT_CSRF_TRUSTED_ORIGINS"] != "":
|
|
427
|
+
CSRF_TRUSTED_ORIGINS = os.getenv("NAUTOBOT_CSRF_TRUSTED_ORIGINS", "").split(_CONFIG_SETTING_SEPARATOR)
|
|
428
|
+
|
|
422
429
|
CSRF_FAILURE_VIEW = "nautobot.core.views.csrf_failure"
|
|
423
430
|
DATE_FORMAT = os.getenv("NAUTOBOT_DATE_FORMAT", "N j, Y")
|
|
424
431
|
DATETIME_FORMAT = os.getenv("NAUTOBOT_DATETIME_FORMAT", "N j, Y g:i a")
|
nautobot/core/settings.yaml
CHANGED
|
@@ -587,6 +587,16 @@ properties:
|
|
|
587
587
|
description: >-
|
|
588
588
|
A list of hosts (fully-qualified domain names (FQDNs) or subdomains) that are considered trusted origins
|
|
589
589
|
for cross-site secure requests such as HTTPS POST.
|
|
590
|
+
|
|
591
|
+
You might need to set this if you are using a reverse proxy or load balancer that changes the host header or if you face the error
|
|
592
|
+
'Invalid Form Submission - CSRF failure has occured' and 'Origin checking failed - https://subdomain.example.com does not match any trusted origins.'
|
|
593
|
+
|
|
594
|
+
Example:
|
|
595
|
+
|
|
596
|
+
```python
|
|
597
|
+
CSRF_TRUSTED_ORIGINS = ['https://subdomain.example.com', 'https://*.example.com']
|
|
598
|
+
```
|
|
599
|
+
environment_variable: "NAUTOBOT_CSRF_TRUSTED_ORIGINS"
|
|
590
600
|
items:
|
|
591
601
|
type: "string"
|
|
592
602
|
see_also:
|
nautobot/core/signals.py
CHANGED
|
@@ -7,7 +7,6 @@ import logging
|
|
|
7
7
|
|
|
8
8
|
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
|
9
9
|
from django.core.cache import cache
|
|
10
|
-
from django.db.models.signals import post_delete, post_save
|
|
11
10
|
from django.dispatch import receiver, Signal
|
|
12
11
|
import redis.exceptions
|
|
13
12
|
|
|
@@ -62,10 +61,12 @@ def disable_for_loaddata(signal_handler):
|
|
|
62
61
|
return wrapper
|
|
63
62
|
|
|
64
63
|
|
|
65
|
-
@receiver(post_save)
|
|
66
|
-
@receiver(post_delete)
|
|
67
64
|
def invalidate_max_depth_cache(sender, **kwargs):
|
|
68
|
-
"""
|
|
65
|
+
"""
|
|
66
|
+
Clear the appropriate TreeManager.max_depth cache as the create/update/delete may have changed the tree.
|
|
67
|
+
|
|
68
|
+
Note that this signal is connected in `TreeModel.__init_subclass__()` so as to only apply to those models.
|
|
69
|
+
"""
|
|
69
70
|
from nautobot.core.models.tree_queries import TreeManager
|
|
70
71
|
|
|
71
72
|
if not isinstance(sender.objects, TreeManager):
|
|
@@ -94,9 +94,13 @@ SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "{{ secret_key }}")
|
|
|
94
94
|
# FQDNs that are considered trusted origins for secure, cross-domain, requests such as HTTPS POST.
|
|
95
95
|
# If running Nautobot under a single domain, you may not need to set this variable;
|
|
96
96
|
# if running on multiple domains, you *may* need to set this variable to more or less the same as ALLOWED_HOSTS above.
|
|
97
|
+
# You also want to set this variable if you are facing CSRF validation issues such as
|
|
98
|
+
# 'CSRF failure has occured' or 'Origin checking failed - https://subdomain.example.com does not match any trusted origins.'
|
|
97
99
|
# https://docs.djangoproject.com/en/stable/ref/settings/#csrf-trusted-origins
|
|
98
100
|
#
|
|
99
101
|
# CSRF_TRUSTED_ORIGINS = []
|
|
102
|
+
# if "NAUTOBOT_CSRF_TRUSTED_ORIGINS" in os.environ and os.environ["NAUTOBOT_CSRF_TRUSTED_ORIGINS"] != "":
|
|
103
|
+
# CSRF_TRUSTED_ORIGINS = os.getenv("NAUTOBOT_CSRF_TRUSTED_ORIGINS", "").split(_CONFIG_SETTING_SEPARATOR)
|
|
100
104
|
|
|
101
105
|
# Date/time formatting. See the following link for supported formats:
|
|
102
106
|
# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date
|
nautobot/dcim/forms.py
CHANGED
|
@@ -13,6 +13,8 @@ from nautobot.core.forms import (
|
|
|
13
13
|
add_blank_choice,
|
|
14
14
|
APISelect,
|
|
15
15
|
APISelectMultiple,
|
|
16
|
+
AutoPositionField,
|
|
17
|
+
AutoPositionPatternField,
|
|
16
18
|
BootstrapMixin,
|
|
17
19
|
BulkEditNullBooleanSelect,
|
|
18
20
|
ColorSelect,
|
|
@@ -269,23 +271,7 @@ class ComponentForm(BootstrapMixin, forms.Form):
|
|
|
269
271
|
|
|
270
272
|
|
|
271
273
|
class ModularComponentForm(ComponentForm):
|
|
272
|
-
|
|
273
|
-
label="Name",
|
|
274
|
-
help_text="""
|
|
275
|
-
Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range
|
|
276
|
-
are not supported. Examples:
|
|
277
|
-
<ul>
|
|
278
|
-
<li><code>[ge,xe]-0/0/[0-9]</code></li>
|
|
279
|
-
<li><code>e[0-3][a-d,f]</code></li>
|
|
280
|
-
</ul>
|
|
281
|
-
|
|
282
|
-
The variables <code>{module}</code>, <code>{module.parent}</code>, <code>{module.parent.parent}</code>, etc.
|
|
283
|
-
may be used in the name field and will be replaced by the <code>position</code> of the module bay that the
|
|
284
|
-
module occupies (skipping over any bays with a blank <code>position</code>). These variables can be used
|
|
285
|
-
multiple times in the component name and there is no limit to the depth of parent levels.
|
|
286
|
-
Any variables that cannot be replaced by a suitable position value will remain unchanged.
|
|
287
|
-
""",
|
|
288
|
-
)
|
|
274
|
+
"""Base class for forms for components that can be assigned to either a device or a module."""
|
|
289
275
|
|
|
290
276
|
|
|
291
277
|
#
|
|
@@ -1064,11 +1050,27 @@ class ComponentTemplateCreateForm(ComponentForm):
|
|
|
1064
1050
|
description = forms.CharField(required=False)
|
|
1065
1051
|
|
|
1066
1052
|
|
|
1067
|
-
class ModularComponentTemplateCreateForm(
|
|
1053
|
+
class ModularComponentTemplateCreateForm(ComponentTemplateCreateForm):
|
|
1068
1054
|
"""
|
|
1069
1055
|
Base form for the creation of modular device component templates (subclassed from ModularComponentTemplateModel).
|
|
1070
1056
|
"""
|
|
1071
1057
|
|
|
1058
|
+
name_pattern = ExpandableNameField(
|
|
1059
|
+
label="Name",
|
|
1060
|
+
help_text="""
|
|
1061
|
+
Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range
|
|
1062
|
+
are not supported. Examples:
|
|
1063
|
+
<ul>
|
|
1064
|
+
<li><code>[ge,xe]-0/0/[0-9]</code></li>
|
|
1065
|
+
<li><code>e[0-3][a-d,f]</code></li>
|
|
1066
|
+
</ul>
|
|
1067
|
+
|
|
1068
|
+
The variables <code>{module}</code>, <code>{module.parent}</code>, <code>{module.parent.parent}</code>, etc.
|
|
1069
|
+
may be used in the name field and will be replaced by the <code>position</code> of the module bay that the
|
|
1070
|
+
module occupies (skipping over any bays with a blank <code>position</code>). These variables can be used
|
|
1071
|
+
multiple times in the component name and there is no limit to the depth of parent levels.
|
|
1072
|
+
Any variables that cannot be replaced by a suitable position value will remain unchanged.""",
|
|
1073
|
+
)
|
|
1072
1074
|
device_type = DynamicModelChoiceField(
|
|
1073
1075
|
queryset=DeviceType.objects.all(),
|
|
1074
1076
|
required=False,
|
|
@@ -1077,7 +1079,6 @@ class ModularComponentTemplateCreateForm(ModularComponentForm):
|
|
|
1077
1079
|
queryset=ModuleType.objects.all(),
|
|
1078
1080
|
required=False,
|
|
1079
1081
|
)
|
|
1080
|
-
description = forms.CharField(required=False)
|
|
1081
1082
|
|
|
1082
1083
|
|
|
1083
1084
|
class ConsolePortTemplateForm(ModularComponentTemplateForm):
|
|
@@ -1570,10 +1571,10 @@ class ModuleBayBaseCreateForm(BootstrapMixin, forms.Form):
|
|
|
1570
1571
|
required=False,
|
|
1571
1572
|
help_text="Alphanumeric ranges are supported. (Must match the number of names being created.)",
|
|
1572
1573
|
)
|
|
1573
|
-
position_pattern =
|
|
1574
|
-
label="Position",
|
|
1574
|
+
position_pattern = AutoPositionPatternField(
|
|
1575
1575
|
required=False,
|
|
1576
|
-
help_text="Alphanumeric ranges are supported. (Must match the number of names being created.)"
|
|
1576
|
+
help_text="Alphanumeric ranges are supported. (Must match the number of names being created.)"
|
|
1577
|
+
" Default to the names of the module bays unless manually supplied by the user.",
|
|
1577
1578
|
)
|
|
1578
1579
|
description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
|
|
1579
1580
|
|
|
@@ -2542,12 +2543,8 @@ class DeviceBulkAddComponentForm(ComponentForm, CustomFieldModelBulkEditFormMixi
|
|
|
2542
2543
|
nullable_fields = []
|
|
2543
2544
|
|
|
2544
2545
|
|
|
2545
|
-
class ModuleBulkAddComponentForm(
|
|
2546
|
+
class ModuleBulkAddComponentForm(DeviceBulkAddComponentForm):
|
|
2546
2547
|
pk = forms.ModelMultipleChoiceField(queryset=Module.objects.all(), widget=forms.MultipleHiddenInput())
|
|
2547
|
-
description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
|
|
2548
|
-
|
|
2549
|
-
class Meta:
|
|
2550
|
-
nullable_fields = []
|
|
2551
2548
|
|
|
2552
2549
|
|
|
2553
2550
|
#
|
|
@@ -3566,6 +3563,12 @@ class ModuleBayFilterForm(NautobotFilterForm):
|
|
|
3566
3563
|
|
|
3567
3564
|
|
|
3568
3565
|
class ModuleBayForm(NautobotModelForm):
|
|
3566
|
+
position = AutoPositionField(
|
|
3567
|
+
max_length=CHARFIELD_MAX_LENGTH,
|
|
3568
|
+
help_text="The position of the module bay within the parent device/module. "
|
|
3569
|
+
"Defaults to the name of the module bay unless overridden.",
|
|
3570
|
+
required=False,
|
|
3571
|
+
)
|
|
3569
3572
|
parent_device = DynamicModelChoiceField(
|
|
3570
3573
|
queryset=Device.objects.all(),
|
|
3571
3574
|
required=False,
|
|
@@ -1280,3 +1280,8 @@ class ModuleBay(PrimaryModel):
|
|
|
1280
1280
|
|
|
1281
1281
|
if not (self.parent_device or self.parent_module):
|
|
1282
1282
|
raise ValidationError("Either parent_device or parent_module must be set")
|
|
1283
|
+
|
|
1284
|
+
# Populate the position field with the name of the module bay if it is not supplied by the user.
|
|
1285
|
+
|
|
1286
|
+
if not self.position:
|
|
1287
|
+
self.position = self.name
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -173,6 +173,7 @@ class DeviceTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
173
173
|
template_code="""{% if record.device_redundancy_group %}<span class="badge badge-default">{{ record.device_redundancy_group_priority|default:'None' }}</span>{% else %}—{% endif %}"""
|
|
174
174
|
)
|
|
175
175
|
controller_managed_device_group = tables.Column(linkify=True)
|
|
176
|
+
software_version = tables.Column(linkify=True, verbose_name="Software Version")
|
|
176
177
|
secrets_group = tables.Column(linkify=True)
|
|
177
178
|
tags = TagColumn(url_name="dcim:device_list")
|
|
178
179
|
|
|
@@ -201,6 +202,7 @@ class DeviceTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
201
202
|
"vc_priority",
|
|
202
203
|
"device_redundancy_group",
|
|
203
204
|
"device_redundancy_group_priority",
|
|
205
|
+
"software_version",
|
|
204
206
|
"controller_managed_device_group",
|
|
205
207
|
"secrets_group",
|
|
206
208
|
"tags",
|
|
@@ -616,7 +618,7 @@ class DeviceModulePowerOutletTable(PowerOutletTable):
|
|
|
616
618
|
row_attrs = {"class": cable_status_color_css}
|
|
617
619
|
|
|
618
620
|
|
|
619
|
-
class BaseInterfaceTable(BaseTable):
|
|
621
|
+
class BaseInterfaceTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
620
622
|
enabled = BooleanColumn()
|
|
621
623
|
ip_addresses = tables.TemplateColumn(
|
|
622
624
|
template_code=INTERFACE_IPADDRESSES,
|
|
@@ -632,7 +634,7 @@ class BaseInterfaceTable(BaseTable):
|
|
|
632
634
|
vrf = tables.Column(linkify=True, verbose_name="VRF")
|
|
633
635
|
|
|
634
636
|
|
|
635
|
-
class InterfaceTable(
|
|
637
|
+
class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpointTable):
|
|
636
638
|
mgmt_only = BooleanColumn()
|
|
637
639
|
tags = TagColumn(url_name="dcim:interface_list")
|
|
638
640
|
|
|
@@ -2849,6 +2849,22 @@ class ModuleBayTestCase(ModularDeviceComponentTestCaseMixin, ModelTestCases.Base
|
|
|
2849
2849
|
self.assertIsNone(child_module_bay.parent)
|
|
2850
2850
|
self.assertIsNone(grandchild_module_bay.parent)
|
|
2851
2851
|
|
|
2852
|
+
def test_position_value_auto_population(self):
|
|
2853
|
+
"""
|
|
2854
|
+
Assert that the value of the module bay position is auto-populated by its name if position is not provided by the user.
|
|
2855
|
+
"""
|
|
2856
|
+
|
|
2857
|
+
module_bay = ModuleBay.objects.create(
|
|
2858
|
+
parent_device=self.device,
|
|
2859
|
+
name="1111",
|
|
2860
|
+
)
|
|
2861
|
+
module_bay.validated_save()
|
|
2862
|
+
self.assertEqual(module_bay.position, module_bay.name)
|
|
2863
|
+
# Test the default value is overriden if the user provides a position value.
|
|
2864
|
+
module_bay.position = "1222"
|
|
2865
|
+
module_bay.validated_save()
|
|
2866
|
+
self.assertEqual(module_bay.position, "1222")
|
|
2867
|
+
|
|
2852
2868
|
|
|
2853
2869
|
class ModuleBayTemplateTestCase(ModularDeviceComponentTemplateTestCaseMixin, ModelTestCases.BaseModelTestCase):
|
|
2854
2870
|
model = ModuleBayTemplate
|
|
@@ -180,7 +180,7 @@ def web_request_context(
|
|
|
180
180
|
Valid choices are in `nautobot.extras.choices.ObjectChangeEventContextChoices`.
|
|
181
181
|
:param request: Optional web request instance, one will be generated if not supplied
|
|
182
182
|
"""
|
|
183
|
-
from nautobot.extras.jobs import enqueue_job_hooks
|
|
183
|
+
from nautobot.extras.jobs import enqueue_job_hooks # prevent circular import
|
|
184
184
|
|
|
185
185
|
valid_contexts = {
|
|
186
186
|
ObjectChangeEventContextChoices.CONTEXT_JOB: JobChangeContext,
|
|
@@ -203,15 +203,14 @@ def web_request_context(
|
|
|
203
203
|
with change_logging(change_context):
|
|
204
204
|
yield request
|
|
205
205
|
finally:
|
|
206
|
-
|
|
206
|
+
jobs_reloaded = False
|
|
207
207
|
# enqueue jobhooks and webhooks, use change_context.change_id in case change_id was not supplied
|
|
208
|
-
for object_change in
|
|
208
|
+
for object_change in (
|
|
209
|
+
ObjectChange.objects.filter(request_id=change_context.change_id).order_by("time").iterator()
|
|
210
|
+
):
|
|
209
211
|
if context != ObjectChangeEventContextChoices.CONTEXT_JOB_HOOK:
|
|
210
|
-
# Make sure JobHooks are up to date (once) before calling them
|
|
211
|
-
|
|
212
|
-
get_jobs(reload=True)
|
|
213
|
-
jobs_refreshed = True
|
|
214
|
-
enqueue_job_hooks(object_change)
|
|
212
|
+
# Make sure JobHooks are up to date (only once) before calling them
|
|
213
|
+
jobs_reloaded |= enqueue_job_hooks(object_change, may_reload_jobs=(not jobs_reloaded))
|
|
215
214
|
enqueue_webhooks(object_change)
|
|
216
215
|
|
|
217
216
|
|
|
@@ -4,6 +4,7 @@ from .git import (
|
|
|
4
4
|
ensure_git_repository,
|
|
5
5
|
get_repo_access_url,
|
|
6
6
|
git_repository_dry_run,
|
|
7
|
+
refresh_job_code_from_repository,
|
|
7
8
|
)
|
|
8
9
|
from .registry import (
|
|
9
10
|
get_datasource_content_choices,
|
|
@@ -20,4 +21,5 @@ __all__ = (
|
|
|
20
21
|
"get_repo_access_url",
|
|
21
22
|
"git_repository_dry_run",
|
|
22
23
|
"refresh_datasource_content",
|
|
24
|
+
"refresh_job_code_from_repository",
|
|
23
25
|
)
|
|
@@ -96,8 +96,11 @@ def get_repo_access_url(repository_record):
|
|
|
96
96
|
obj=repository_record,
|
|
97
97
|
)
|
|
98
98
|
except ObjectDoesNotExist:
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
logger.warning(
|
|
100
|
+
"HTTP Token not found for secrets group %s associated with repository %s",
|
|
101
|
+
repository_record.secrets_group,
|
|
102
|
+
repository_record,
|
|
103
|
+
)
|
|
101
104
|
try:
|
|
102
105
|
user = repository_record.secrets_group.get_secret_value(
|
|
103
106
|
SecretsGroupAccessTypeChoices.TYPE_HTTP,
|
|
@@ -105,8 +108,12 @@ def get_repo_access_url(repository_record):
|
|
|
105
108
|
obj=repository_record,
|
|
106
109
|
)
|
|
107
110
|
except ObjectDoesNotExist:
|
|
108
|
-
#
|
|
109
|
-
|
|
111
|
+
# May not be needed for this repository, so just log as debug rather than warning
|
|
112
|
+
logger.debug(
|
|
113
|
+
"HTTP Username not found for secrets group %s associated with repository %s",
|
|
114
|
+
repository_record.secrets_group,
|
|
115
|
+
repository_record,
|
|
116
|
+
)
|
|
110
117
|
|
|
111
118
|
if token and token not in from_url:
|
|
112
119
|
# Some git repositories require a user as well as a token.
|
|
@@ -147,7 +154,7 @@ def ensure_git_repository(repository_record, logger=None, head=None): # pylint:
|
|
|
147
154
|
(bool): Whether any change to the local repo actually occurred.
|
|
148
155
|
"""
|
|
149
156
|
# We want to check if the repo is already checked out at head. We also want to avoid calling
|
|
150
|
-
#
|
|
157
|
+
# get_repo_from_url_to_path_and_from_branch, because it will cause the URL to be rebuilt causing calls to a secrets
|
|
151
158
|
# backend. As such, if head is None, we can't perform these checks.
|
|
152
159
|
if head is not None:
|
|
153
160
|
# If the repo exists and has HEAD already checked out, the repo is present and has the correct branch selected.
|
|
@@ -205,7 +212,6 @@ def git_repository_dry_run(repository_record, logger): # pylint: disable=redefi
|
|
|
205
212
|
logger.info("%s - `%s`", item.status, item.text)
|
|
206
213
|
else:
|
|
207
214
|
logger.info("Repository has no changes")
|
|
208
|
-
|
|
209
215
|
except Exception as exc:
|
|
210
216
|
logger.error(str(exc))
|
|
211
217
|
raise
|
|
@@ -435,19 +441,12 @@ def import_config_context(context_data, repository_record, job_result):
|
|
|
435
441
|
created = False
|
|
436
442
|
modified = False
|
|
437
443
|
save_needed = False
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
except ConfigContext.DoesNotExist:
|
|
445
|
-
context_record = ConfigContext(
|
|
446
|
-
name=context_metadata.get("name"),
|
|
447
|
-
owner_content_type=git_repository_content_type,
|
|
448
|
-
owner_object_id=repository_record.pk,
|
|
449
|
-
)
|
|
450
|
-
created = True
|
|
444
|
+
context_record, created = ConfigContext.objects.get_or_create(
|
|
445
|
+
name=context_metadata.get("name"),
|
|
446
|
+
owner_content_type=git_repository_content_type,
|
|
447
|
+
owner_object_id=repository_record.pk,
|
|
448
|
+
defaults={"data": {}},
|
|
449
|
+
)
|
|
451
450
|
|
|
452
451
|
for field in ("weight", "description", "is_active"):
|
|
453
452
|
new_value = context_metadata[field]
|
|
@@ -662,20 +661,12 @@ def import_config_context_schema(context_schema_data, repository_record, job_res
|
|
|
662
661
|
|
|
663
662
|
schema_metadata = context_schema_data["_metadata"]
|
|
664
663
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
except ConfigContextSchema.DoesNotExist:
|
|
672
|
-
schema_record = ConfigContextSchema(
|
|
673
|
-
name=schema_metadata["name"],
|
|
674
|
-
owner_content_type=git_repository_content_type,
|
|
675
|
-
owner_object_id=repository_record.pk,
|
|
676
|
-
data_schema=context_schema_data["data_schema"],
|
|
677
|
-
)
|
|
678
|
-
created = True
|
|
664
|
+
schema_record, created = ConfigContextSchema.objects.get_or_create(
|
|
665
|
+
name=schema_metadata["name"],
|
|
666
|
+
owner_content_type=git_repository_content_type,
|
|
667
|
+
owner_object_id=repository_record.pk,
|
|
668
|
+
defaults={"data_schema": context_schema_data["data_schema"]},
|
|
669
|
+
)
|
|
679
670
|
|
|
680
671
|
if schema_record.description != schema_metadata.get("description", ""):
|
|
681
672
|
schema_record.description = schema_metadata.get("description", "")
|
|
@@ -868,22 +859,12 @@ def update_git_export_templates(repository_record, job_result):
|
|
|
868
859
|
# To reduce noise until the base issue is fixed, we need to explicitly detect object changes:
|
|
869
860
|
created = False
|
|
870
861
|
modified = False
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
)
|
|
878
|
-
except ExportTemplate.DoesNotExist:
|
|
879
|
-
template_record = ExportTemplate(
|
|
880
|
-
content_type=model_content_type,
|
|
881
|
-
name=file_name,
|
|
882
|
-
owner_content_type=git_repository_content_type,
|
|
883
|
-
owner_object_id=repository_record.pk,
|
|
884
|
-
)
|
|
885
|
-
created = True
|
|
886
|
-
modified = True
|
|
862
|
+
template_record, created = ExportTemplate.objects.get_or_create(
|
|
863
|
+
content_type=model_content_type,
|
|
864
|
+
name=file_name,
|
|
865
|
+
owner_content_type=git_repository_content_type,
|
|
866
|
+
owner_object_id=repository_record.pk,
|
|
867
|
+
)
|
|
887
868
|
|
|
888
869
|
if template_record.template_code != template_content:
|
|
889
870
|
template_record.template_code = template_content
|
|
@@ -32,7 +32,7 @@ def refresh_datasource_content(model_name, record, user, job_result, delete=Fals
|
|
|
32
32
|
job_result (JobResult): Passed through to the callback functions to use with logging their actions.
|
|
33
33
|
delete (bool): True if the record is being deleted; False if it is being created/updated.
|
|
34
34
|
"""
|
|
35
|
-
job_result.log(f"Refreshing data provided by {record}...", level_choice=LogLevelChoices.LOG_INFO)
|
|
35
|
+
job_result.log(f"Refreshing data provided by {record}...", level_choice=LogLevelChoices.LOG_INFO, obj=record)
|
|
36
36
|
|
|
37
37
|
for entry in get_datasource_contents(model_name):
|
|
38
38
|
job_result.log(f"Refreshing {entry.name}...", level_choice=LogLevelChoices.LOG_INFO)
|
|
@@ -54,4 +54,4 @@ def refresh_datasource_content(model_name, record, user, job_result, delete=Fals
|
|
|
54
54
|
raise RuntimeError(msg)
|
|
55
55
|
|
|
56
56
|
# Otherwise, log a friendly info message.
|
|
57
|
-
job_result.log(f"Data refresh from {record} complete!", level_choice=LogLevelChoices.LOG_INFO)
|
|
57
|
+
job_result.log(f"Data refresh from {record} complete!", level_choice=LogLevelChoices.LOG_INFO, obj=record)
|
nautobot/extras/jobs.py
CHANGED
|
@@ -1144,20 +1144,23 @@ def run_job(self, job_class_path, *args, **kwargs):
|
|
|
1144
1144
|
raise
|
|
1145
1145
|
|
|
1146
1146
|
|
|
1147
|
-
def enqueue_job_hooks(object_change):
|
|
1147
|
+
def enqueue_job_hooks(object_change, may_reload_jobs=True):
|
|
1148
1148
|
"""
|
|
1149
|
-
Find job hook(s) assigned to this changed object type + action and enqueue them
|
|
1150
|
-
|
|
1149
|
+
Find job hook(s) assigned to this changed object type + action and enqueue them to be processed.
|
|
1150
|
+
|
|
1151
|
+
Returns:
|
|
1152
|
+
jobs_reloaded (bool): whether Jobs were reloaded to make this happen
|
|
1151
1153
|
"""
|
|
1154
|
+
jobs_reloaded = False
|
|
1152
1155
|
|
|
1153
1156
|
# Job hooks cannot trigger other job hooks
|
|
1154
1157
|
if object_change.change_context == ObjectChangeEventContextChoices.CONTEXT_JOB_HOOK:
|
|
1155
|
-
return
|
|
1158
|
+
return jobs_reloaded
|
|
1156
1159
|
|
|
1157
1160
|
# Determine whether this type of object supports job hooks
|
|
1158
1161
|
content_type = object_change.changed_object_type
|
|
1159
1162
|
if content_type not in change_logged_models_queryset():
|
|
1160
|
-
return
|
|
1163
|
+
return jobs_reloaded
|
|
1161
1164
|
|
|
1162
1165
|
# Retrieve any applicable job hooks
|
|
1163
1166
|
action_flag = {
|
|
@@ -1167,7 +1170,14 @@ def enqueue_job_hooks(object_change):
|
|
|
1167
1170
|
}[object_change.action]
|
|
1168
1171
|
job_hooks = JobHook.objects.filter(content_types=content_type, enabled=True, **{action_flag: True})
|
|
1169
1172
|
|
|
1173
|
+
if not job_hooks.exists():
|
|
1174
|
+
return jobs_reloaded
|
|
1175
|
+
|
|
1170
1176
|
# Enqueue the jobs related to the job_hooks
|
|
1177
|
+
if may_reload_jobs:
|
|
1178
|
+
get_jobs(reload=True)
|
|
1179
|
+
jobs_reloaded = True
|
|
1180
|
+
|
|
1171
1181
|
for job_hook in job_hooks:
|
|
1172
1182
|
job_model = job_hook.job
|
|
1173
1183
|
if not job_model.installed or not job_model.enabled:
|
|
@@ -1178,3 +1188,5 @@ def enqueue_job_hooks(object_change):
|
|
|
1178
1188
|
logger.error("JobHook %s is enabled, but the underlying Job implementation is missing", job_hook)
|
|
1179
1189
|
else:
|
|
1180
1190
|
JobResult.enqueue_job(job_model, object_change.user, object_change=object_change.pk)
|
|
1191
|
+
|
|
1192
|
+
return jobs_reloaded
|
|
@@ -115,6 +115,12 @@ class GitRepository(PrimaryModel):
|
|
|
115
115
|
"provides contents overlapping with this repository."
|
|
116
116
|
)
|
|
117
117
|
|
|
118
|
+
# Changing branch or remote_url invalidates current_head
|
|
119
|
+
if self.present_in_database:
|
|
120
|
+
past = GitRepository.objects.get(id=self.id)
|
|
121
|
+
if self.remote_url != past.remote_url or self.branch != past.branch:
|
|
122
|
+
self.current_head = ""
|
|
123
|
+
|
|
118
124
|
def get_latest_sync(self):
|
|
119
125
|
"""
|
|
120
126
|
Return a `JobResult` for the latest sync operation.
|