nautobot 2.1.7__py3-none-any.whl → 2.1.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/api.py +1 -2
- nautobot/apps/utils.py +4 -0
- nautobot/apps/views.py +2 -0
- nautobot/circuits/api/urls.py +1 -2
- nautobot/circuits/api/views.py +0 -12
- nautobot/circuits/tests/integration/test_relationships.py +0 -4
- nautobot/core/api/routers.py +25 -3
- nautobot/core/api/utils.py +4 -0
- nautobot/core/api/views.py +21 -15
- nautobot/core/celery/schedulers.py +13 -0
- nautobot/core/choices.py +0 -21
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/tree_queries.py +29 -7
- nautobot/core/releases.py +1 -1
- nautobot/core/settings.py +9 -0
- nautobot/core/settings_funcs.py +0 -18
- nautobot/core/signals.py +5 -5
- nautobot/core/tasks.py +7 -3
- nautobot/core/templates/admin/base.html +23 -94
- nautobot/core/templates/generic/object_list.html +2 -0
- nautobot/core/templates/graphene/graphiql.html +18 -47
- nautobot/core/templates/inc/footer.html +5 -5
- nautobot/core/templates/inc/nav_menu.html +0 -7
- nautobot/core/templates/nautobot_config.py.j2 +6 -0
- nautobot/core/templates/rest_framework/api.html +12 -5
- nautobot/core/testing/mixins.py +13 -5
- nautobot/core/tests/integration/test_plugin_navbar.py +7 -21
- nautobot/core/tests/integration/test_view_authentication.py +67 -0
- nautobot/core/tests/runner.py +25 -2
- nautobot/core/tests/test_graphql.py +2 -14
- nautobot/core/tests/test_models.py +3 -3
- nautobot/core/tests/test_navigations.py +67 -10
- nautobot/core/tests/test_releases.py +9 -3
- nautobot/core/tests/test_views.py +23 -16
- nautobot/core/utils/lookup.py +124 -0
- nautobot/core/views/__init__.py +3 -7
- nautobot/core/views/generic.py +9 -0
- nautobot/dcim/api/urls.py +1 -2
- nautobot/dcim/api/views.py +1 -12
- nautobot/dcim/choices.py +56 -0
- nautobot/dcim/models/racks.py +1 -3
- nautobot/dcim/navigation.py +1 -1
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +67 -43
- nautobot/dcim/tests/test_api.py +3 -0
- nautobot/dcim/tests/test_filters.py +0 -28
- nautobot/dcim/views.py +5 -2
- nautobot/extras/api/urls.py +1 -2
- nautobot/extras/api/views.py +0 -10
- nautobot/extras/choices.py +14 -0
- nautobot/extras/models/customfields.py +93 -34
- nautobot/extras/models/groups.py +1 -1
- nautobot/extras/models/relationships.py +32 -19
- nautobot/extras/navigation.py +3 -2
- nautobot/extras/plugins/__init__.py +8 -0
- nautobot/extras/plugins/views.py +6 -9
- nautobot/extras/querysets.py +1 -1
- nautobot/extras/signals.py +12 -6
- nautobot/extras/templates/extras/customfield.html +22 -14
- nautobot/extras/templatetags/job_buttons.py +7 -0
- nautobot/extras/templatetags/plugins.py +5 -1
- nautobot/extras/tests/test_customfields.py +323 -287
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_jobs.py +2 -2
- nautobot/extras/tests/test_plugins.py +41 -0
- nautobot/extras/tests/test_relationships.py +31 -14
- nautobot/extras/tests/test_views.py +124 -1
- nautobot/extras/utils.py +7 -3
- nautobot/extras/views.py +10 -10
- nautobot/ipam/api/urls.py +1 -2
- nautobot/ipam/api/views.py +6 -13
- nautobot/ipam/tables.py +0 -1
- nautobot/ipam/tests/test_graphql.py +2 -3
- nautobot/ipam/views.py +12 -10
- nautobot/project-static/css/base.css +1 -0
- nautobot/project-static/docs/404.html +30 -2
- nautobot/project-static/docs/apps/index.html +30 -2
- nautobot/project-static/docs/apps/nautobot-apps.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +410 -410
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +386 -358
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +45 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +759 -602
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +528 -467
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +205 -109
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +30 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +1265 -785
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1827 -1746
- nautobot/project-static/docs/development/apps/api/configuration-view.html +30 -2
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/global-search.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/graphql.html +30 -2
- nautobot/project-static/docs/development/apps/api/models/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +31 -3
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +30 -2
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +30 -2
- nautobot/project-static/docs/development/apps/api/prometheus.html +30 -2
- nautobot/project-static/docs/development/apps/api/setup.html +30 -2
- nautobot/project-static/docs/development/apps/api/testing.html +33 -5
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +30 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +30 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +33 -5
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-detail-views.html +13 -5559
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +5594 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/tabs.html +3 -3
- nautobot/project-static/docs/development/apps/api/views/base-template.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +44 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +47 -14
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/index.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/notes.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +30 -2
- nautobot/project-static/docs/development/apps/api/views/urls.html +30 -2
- nautobot/project-static/docs/development/apps/index.html +30 -2
- nautobot/project-static/docs/development/apps/migration/code-updates.html +30 -2
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +30 -2
- nautobot/project-static/docs/development/apps/migration/from-v1.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +30 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +30 -2
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +30 -2
- nautobot/project-static/docs/development/core/application-registry.html +30 -2
- nautobot/project-static/docs/development/core/best-practices.html +33 -5
- nautobot/project-static/docs/development/core/bootstrap-ui.html +30 -2
- nautobot/project-static/docs/development/core/caching.html +5481 -0
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +30 -2
- nautobot/project-static/docs/development/core/extending-models.html +33 -5
- nautobot/project-static/docs/development/core/generic-views.html +30 -2
- nautobot/project-static/docs/development/core/getting-started.html +49 -12
- nautobot/project-static/docs/development/core/homepage.html +30 -2
- nautobot/project-static/docs/development/core/index.html +30 -2
- nautobot/project-static/docs/development/core/model-features.html +30 -2
- nautobot/project-static/docs/development/core/natural-keys.html +30 -2
- nautobot/project-static/docs/development/core/navigation-menu.html +30 -2
- nautobot/project-static/docs/development/core/release-checklist.html +30 -2
- nautobot/project-static/docs/development/core/role-internals.html +30 -2
- nautobot/project-static/docs/development/core/style-guide.html +30 -2
- nautobot/project-static/docs/development/core/templates.html +30 -2
- nautobot/project-static/docs/development/core/testing.html +30 -2
- nautobot/project-static/docs/development/core/user-preferences.html +30 -2
- nautobot/project-static/docs/development/index.html +30 -2
- nautobot/project-static/docs/development/jobs/index.html +30 -2
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +30 -2
- nautobot/project-static/docs/index.html +30 -2
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/index.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.0.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.1.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.2.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.3.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.4.html +31 -3
- nautobot/project-static/docs/release-notes/version-1.5.html +30 -2
- nautobot/project-static/docs/release-notes/version-1.6.html +573 -134
- nautobot/project-static/docs/release-notes/version-2.0.html +30 -2
- nautobot/project-static/docs/release-notes/version-2.1.html +539 -170
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +250 -240
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +49 -2
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +30 -2
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +30 -2
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +37 -5
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +6019 -0
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/index.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +30 -2
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +33 -5
- nautobot/project-static/docs/user-guide/administration/installation/services.html +30 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +30 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +30 -2
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +30 -2
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +30 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +30 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +33 -5
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +30 -2
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +30 -2
- nautobot/project-static/docs/user-guide/index.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +111 -15
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +30 -2
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +30 -2
- nautobot/tenancy/api/urls.py +1 -2
- nautobot/tenancy/api/views.py +0 -12
- nautobot/tenancy/navigation.py +1 -1
- nautobot/tenancy/tests/test_filters.py +0 -168
- nautobot/users/api/urls.py +1 -2
- nautobot/users/api/views.py +2 -65
- nautobot/users/views.py +8 -8
- nautobot/virtualization/api/urls.py +1 -2
- nautobot/virtualization/api/views.py +0 -12
- nautobot/virtualization/tests/test_filters.py +0 -28
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/METADATA +2 -2
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/RECORD +338 -334
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/NOTICE +0 -0
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/WHEEL +0 -0
- {nautobot-2.1.7.dist-info → nautobot-2.1.9.dist-info}/entry_points.txt +0 -0
nautobot/apps/api.py
CHANGED
|
@@ -19,7 +19,7 @@ from nautobot.core.api.fields import (
|
|
|
19
19
|
)
|
|
20
20
|
from nautobot.core.api.mixins import WritableSerializerMixin
|
|
21
21
|
from nautobot.core.api.parsers import NautobotCSVParser
|
|
22
|
-
from nautobot.core.api.routers import OrderedDefaultRouter
|
|
22
|
+
from nautobot.core.api.routers import AuthenticatedAPIRootView as APIRootView, OrderedDefaultRouter
|
|
23
23
|
from nautobot.core.api.schema import NautobotAutoSchema
|
|
24
24
|
from nautobot.core.api.serializers import (
|
|
25
25
|
OptInFieldsMixin,
|
|
@@ -36,7 +36,6 @@ from nautobot.core.api.utils import (
|
|
|
36
36
|
versioned_serializer_selector,
|
|
37
37
|
)
|
|
38
38
|
from nautobot.core.api.views import (
|
|
39
|
-
APIRootView,
|
|
40
39
|
BulkDestroyModelMixin,
|
|
41
40
|
BulkUpdateModelMixin,
|
|
42
41
|
GetObjectCountsView,
|
nautobot/apps/utils.py
CHANGED
|
@@ -32,6 +32,8 @@ from nautobot.core.utils.lookup import (
|
|
|
32
32
|
get_related_class_for_model,
|
|
33
33
|
get_route_for_model,
|
|
34
34
|
get_table_for_model,
|
|
35
|
+
get_url_for_url_pattern,
|
|
36
|
+
get_url_patterns,
|
|
35
37
|
)
|
|
36
38
|
from nautobot.core.utils.navigation import (
|
|
37
39
|
get_all_new_ui_ready_routes,
|
|
@@ -110,6 +112,8 @@ __all__ = (
|
|
|
110
112
|
"get_route_for_model",
|
|
111
113
|
"get_settings_or_config",
|
|
112
114
|
"get_table_for_model",
|
|
115
|
+
"get_url_for_url_pattern",
|
|
116
|
+
"get_url_patterns",
|
|
113
117
|
"get_worker_count",
|
|
114
118
|
"GitRepo",
|
|
115
119
|
"hex_to_rgb",
|
nautobot/apps/views.py
CHANGED
|
@@ -8,6 +8,7 @@ from nautobot.core.views.generic import (
|
|
|
8
8
|
BulkImportView,
|
|
9
9
|
BulkRenameView,
|
|
10
10
|
ComponentCreateView,
|
|
11
|
+
GenericView,
|
|
11
12
|
ObjectDeleteView,
|
|
12
13
|
ObjectEditView,
|
|
13
14
|
ObjectImportView,
|
|
@@ -57,6 +58,7 @@ __all__ = (
|
|
|
57
58
|
"csv_format",
|
|
58
59
|
"EnhancedPage",
|
|
59
60
|
"EnhancedPaginator",
|
|
61
|
+
"GenericView",
|
|
60
62
|
"get_csv_form_fields_from_serializer_class",
|
|
61
63
|
"get_paginate_count",
|
|
62
64
|
"GetReturnURLMixin",
|
nautobot/circuits/api/urls.py
CHANGED
|
@@ -2,8 +2,7 @@ from nautobot.core.api.routers import OrderedDefaultRouter
|
|
|
2
2
|
|
|
3
3
|
from . import views
|
|
4
4
|
|
|
5
|
-
router = OrderedDefaultRouter()
|
|
6
|
-
router.APIRootView = views.CircuitsRootView
|
|
5
|
+
router = OrderedDefaultRouter(view_name="Circuits")
|
|
7
6
|
|
|
8
7
|
# Providers
|
|
9
8
|
router.register("providers", views.ProviderViewSet)
|
nautobot/circuits/api/views.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from rest_framework.routers import APIRootView
|
|
2
|
-
|
|
3
1
|
from nautobot.circuits import filters
|
|
4
2
|
from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider, ProviderNetwork
|
|
5
3
|
from nautobot.core.models.querysets import count_related
|
|
@@ -8,16 +6,6 @@ from nautobot.extras.api.views import NautobotModelViewSet
|
|
|
8
6
|
|
|
9
7
|
from . import serializers
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
class CircuitsRootView(APIRootView):
|
|
13
|
-
"""
|
|
14
|
-
Circuits API root view
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
def get_view_name(self):
|
|
18
|
-
return "Circuits"
|
|
19
|
-
|
|
20
|
-
|
|
21
9
|
#
|
|
22
10
|
# Providers
|
|
23
11
|
#
|
|
@@ -110,10 +110,6 @@ class CircuitRelationshipsTestCase(SeleniumTestCase):
|
|
|
110
110
|
destination_type=fake_ct,
|
|
111
111
|
destination_id=uuid.uuid4(),
|
|
112
112
|
)
|
|
113
|
-
# Clear the server-side cache of relationship information to make sure it gets refreshed properly
|
|
114
|
-
Relationship.objects.get_for_model.cache_clear()
|
|
115
|
-
Relationship.objects.get_for_model_source.cache_clear()
|
|
116
|
-
Relationship.objects.get_for_model_destination.cache_clear()
|
|
117
113
|
|
|
118
114
|
def tearDown(self):
|
|
119
115
|
self.logout()
|
nautobot/core/api/routers.py
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
import logging
|
|
2
2
|
|
|
3
3
|
from rest_framework.routers import DefaultRouter
|
|
4
4
|
|
|
5
|
+
from nautobot.core.api.views import AuthenticatedAPIRootView
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class OrderedDefaultRouter(DefaultRouter):
|
|
7
|
-
|
|
11
|
+
APIRootView = AuthenticatedAPIRootView
|
|
12
|
+
|
|
13
|
+
def __init__(self, *args, view_name=None, view_description=None, **kwargs):
|
|
8
14
|
super().__init__(*args, **kwargs)
|
|
9
15
|
|
|
16
|
+
self.view_name = view_name
|
|
17
|
+
if view_name and not view_description:
|
|
18
|
+
view_description = f"{view_name} API root view"
|
|
19
|
+
self.view_description = view_description
|
|
20
|
+
|
|
10
21
|
# Extend the list view mappings to support the DELETE operation
|
|
11
22
|
self.routes[0].mapping.update(
|
|
12
23
|
{
|
|
@@ -20,9 +31,20 @@ class OrderedDefaultRouter(DefaultRouter):
|
|
|
20
31
|
"""
|
|
21
32
|
Wrap DRF's DefaultRouter to return an alphabetized list of endpoints.
|
|
22
33
|
"""
|
|
23
|
-
api_root_dict =
|
|
34
|
+
api_root_dict = {}
|
|
24
35
|
list_name = self.routes[0].name
|
|
36
|
+
|
|
25
37
|
for prefix, _viewset, basename in sorted(self.registry, key=lambda x: x[0]):
|
|
26
38
|
api_root_dict[prefix] = list_name.format(basename=basename)
|
|
27
39
|
|
|
40
|
+
if issubclass(self.APIRootView, AuthenticatedAPIRootView):
|
|
41
|
+
return self.APIRootView.as_view(
|
|
42
|
+
api_root_dict=api_root_dict, name=self.view_name, description=self.view_description
|
|
43
|
+
)
|
|
44
|
+
# Fallback for the established practice of overriding self.APIRootView with a custom class
|
|
45
|
+
logger.warning(
|
|
46
|
+
"Something has changed an OrderedDefaultRouter's APIRootView attribute to a custom class. "
|
|
47
|
+
"Please verify that class %s implements appropriate authentication controls.",
|
|
48
|
+
self.APIRootView.__name__,
|
|
49
|
+
)
|
|
28
50
|
return self.APIRootView.as_view(api_root_dict=api_root_dict)
|
nautobot/core/api/utils.py
CHANGED
|
@@ -178,6 +178,10 @@ def get_view_name(view, suffix=None):
|
|
|
178
178
|
|
|
179
179
|
else:
|
|
180
180
|
# Replicate DRF's built-in behavior.
|
|
181
|
+
name = getattr(view, "name", None)
|
|
182
|
+
if name is not None:
|
|
183
|
+
return view.name
|
|
184
|
+
|
|
181
185
|
name = view.__class__.__name__
|
|
182
186
|
name = formatting.remove_trailing_string(name, "View")
|
|
183
187
|
name = formatting.remove_trailing_string(name, "ViewSet")
|
nautobot/core/api/views.py
CHANGED
|
@@ -22,9 +22,9 @@ from graphql import get_default_backend
|
|
|
22
22
|
from graphql.execution import ExecutionResult
|
|
23
23
|
from graphql.execution.middleware import MiddlewareManager
|
|
24
24
|
from graphql.type.schema import GraphQLSchema
|
|
25
|
-
from rest_framework import status
|
|
25
|
+
from rest_framework import routers, status
|
|
26
26
|
from rest_framework.exceptions import ParseError, PermissionDenied
|
|
27
|
-
from rest_framework.permissions import
|
|
27
|
+
from rest_framework.permissions import IsAuthenticated
|
|
28
28
|
from rest_framework.response import Response
|
|
29
29
|
from rest_framework.reverse import reverse
|
|
30
30
|
from rest_framework.views import APIView
|
|
@@ -341,15 +341,25 @@ class ReadOnlyModelViewSet(NautobotAPIVersionMixin, ModelViewSetMixin, ReadOnlyM
|
|
|
341
341
|
#
|
|
342
342
|
|
|
343
343
|
|
|
344
|
-
class
|
|
344
|
+
class AuthenticatedAPIRootView(NautobotAPIVersionMixin, routers.APIRootView):
|
|
345
345
|
"""
|
|
346
|
-
|
|
346
|
+
Extends DRF's base APIRootView class to enforce user authentication.
|
|
347
347
|
"""
|
|
348
348
|
|
|
349
|
-
|
|
349
|
+
permission_classes = [IsAuthenticated]
|
|
350
|
+
|
|
351
|
+
name = None
|
|
352
|
+
description = None
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class APIRootView(AuthenticatedAPIRootView):
|
|
356
|
+
"""
|
|
357
|
+
This is the root of the REST API.
|
|
350
358
|
|
|
351
|
-
|
|
352
|
-
|
|
359
|
+
API endpoints are arranged by app and model name; e.g. `/api/dcim/locations/`.
|
|
360
|
+
"""
|
|
361
|
+
|
|
362
|
+
name = "API Root"
|
|
353
363
|
|
|
354
364
|
@extend_schema(exclude=True)
|
|
355
365
|
def get(self, request, format=None): # pylint: disable=redefined-builtin
|
|
@@ -492,11 +502,6 @@ class NautobotSpectacularSwaggerView(APIVersioningGetSchemaURLMixin, Spectacular
|
|
|
492
502
|
@extend_schema(exclude=True)
|
|
493
503
|
def get(self, request, *args, **kwargs):
|
|
494
504
|
"""Fix up the rendering of the Swagger UI to work with Nautobot's UI."""
|
|
495
|
-
if not request.user.is_authenticated:
|
|
496
|
-
doc_url = reverse("api_docs")
|
|
497
|
-
login_url = reverse(settings.LOGIN_URL)
|
|
498
|
-
return redirect(f"{login_url}?next={doc_url}")
|
|
499
|
-
|
|
500
505
|
# For backward compatibility wtih drf-yasg, `/api/docs/?format=openapi` is a redirect to the JSON schema.
|
|
501
506
|
if request.GET.get("format") == "openapi":
|
|
502
507
|
return redirect("schema_json", permanent=True)
|
|
@@ -525,11 +530,12 @@ class NautobotSpectacularRedocView(APIVersioningGetSchemaURLMixin, SpectacularRe
|
|
|
525
530
|
class GraphQLDRFAPIView(NautobotAPIVersionMixin, APIView):
|
|
526
531
|
"""
|
|
527
532
|
API View for GraphQL to integrate properly with DRF authentication mechanism.
|
|
528
|
-
The code is a stripped down version of graphene-django default View
|
|
529
|
-
https://github.com/graphql-python/graphene-django/blob/main/graphene_django/views.py#L57
|
|
530
533
|
"""
|
|
531
534
|
|
|
532
|
-
|
|
535
|
+
# The code is a stripped down version of graphene-django default View
|
|
536
|
+
# https://github.com/graphql-python/graphene-django/blob/main/graphene_django/views.py#L57
|
|
537
|
+
|
|
538
|
+
permission_classes = [IsAuthenticated]
|
|
533
539
|
graphql_schema = None
|
|
534
540
|
executor = None
|
|
535
541
|
backend = None
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from collections.abc import Mapping
|
|
2
2
|
import logging
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from celery import current_app
|
|
6
|
+
from django.conf import settings
|
|
5
7
|
from django_celery_beat.schedulers import DatabaseScheduler, ModelEntry
|
|
6
8
|
from kombu.utils.json import loads
|
|
7
9
|
|
|
@@ -94,3 +96,14 @@ class NautobotDatabaseScheduler(DatabaseScheduler):
|
|
|
94
96
|
entry.total_run_count = entry.model.total_run_count
|
|
95
97
|
entry.model.save()
|
|
96
98
|
return resp
|
|
99
|
+
|
|
100
|
+
def tick(self, *args, **kwargs):
|
|
101
|
+
"""
|
|
102
|
+
Run a tick - one iteration of the scheduler.
|
|
103
|
+
|
|
104
|
+
This is an extension of `celery.beat.Scheduler.tick()` to touch the `CELERY_BEAT_HEARTBEAT_FILE` file.
|
|
105
|
+
"""
|
|
106
|
+
interval = super().tick(*args, **kwargs)
|
|
107
|
+
if settings.CELERY_BEAT_HEARTBEAT_FILE:
|
|
108
|
+
Path(settings.CELERY_BEAT_HEARTBEAT_FILE).touch(exist_ok=True)
|
|
109
|
+
return interval
|
nautobot/core/choices.py
CHANGED
|
@@ -11,27 +11,6 @@ class ChoiceSetMeta(type):
|
|
|
11
11
|
choices = getattr(cls, "CHOICES", ())
|
|
12
12
|
return iter(choices)
|
|
13
13
|
|
|
14
|
-
def __getattribute__(cls, attr):
|
|
15
|
-
"""
|
|
16
|
-
Overrides the default __getattribute__ method to provide custom behavior when accessing attributes of a ChoiceSet
|
|
17
|
-
instance. If the attribute name is an uppercase string and not equal to 'CHOICES', this method returns a new class
|
|
18
|
-
instance of the same type as the original attribute value, but with an additional 'label' property. This 'label'
|
|
19
|
-
property looks up the choice label for the given attribute value in the 'CHOICES' sequence and returns it.
|
|
20
|
-
"""
|
|
21
|
-
value = super().__getattribute__(attr)
|
|
22
|
-
# Check if the attribute is a member of the CHOICES sequence and is uppercase;
|
|
23
|
-
# Mostly only ChoiceSet choices are uppercase
|
|
24
|
-
if attr != "CHOICES" and attr.isupper():
|
|
25
|
-
choices = cls.as_dict()
|
|
26
|
-
|
|
27
|
-
class Choice(value.__class__):
|
|
28
|
-
@property
|
|
29
|
-
def label(self):
|
|
30
|
-
return choices.get(self)
|
|
31
|
-
|
|
32
|
-
return Choice(value)
|
|
33
|
-
return value
|
|
34
|
-
|
|
35
14
|
|
|
36
15
|
class ChoiceSet(metaclass=ChoiceSetMeta):
|
|
37
16
|
"""
|
nautobot/core/models/__init__.py
CHANGED
|
@@ -94,7 +94,7 @@ class BaseModel(models.Model):
|
|
|
94
94
|
|
|
95
95
|
Necessary for use with _content_type_cached and management commands.
|
|
96
96
|
"""
|
|
97
|
-
return f"{cls._meta.label_lower}._content_type"
|
|
97
|
+
return f"nautobot.{cls._meta.label_lower}._content_type"
|
|
98
98
|
|
|
99
99
|
@classproperty # https://github.com/PyCQA/pylint-django/issues/240
|
|
100
100
|
def _content_type_cached(cls): # pylint: disable=no-self-argument
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from functools import cached_property
|
|
2
|
-
|
|
3
1
|
from django.core.cache import cache
|
|
4
2
|
from django.db.models import Case, When
|
|
5
3
|
from tree_queries.models import TreeNode
|
|
@@ -41,8 +39,20 @@ class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
|
|
|
41
39
|
return model_class.objects.without_tree_fields().filter(pk__in=ancestor_pks).order_by(preserve_order)
|
|
42
40
|
|
|
43
41
|
def max_tree_depth(self):
|
|
44
|
-
"""
|
|
45
|
-
Get the maximum depth of any
|
|
42
|
+
r"""
|
|
43
|
+
Get the maximum tree depth of any node in this queryset.
|
|
44
|
+
|
|
45
|
+
In most cases you should use TreeManager.max_depth instead as it's cached and this is not.
|
|
46
|
+
|
|
47
|
+
root - depth 0
|
|
48
|
+
\
|
|
49
|
+
branch - depth 1
|
|
50
|
+
\
|
|
51
|
+
leaf - depth 2
|
|
52
|
+
|
|
53
|
+
Note that a queryset with only root nodes will return zero, and an empty queryset will also return zero.
|
|
54
|
+
This is probably a bug, we should really return -1 in the case of an empty queryset, but this is
|
|
55
|
+
"working as implemented" and changing it would possibly be a breaking change at this point.
|
|
46
56
|
"""
|
|
47
57
|
deepest = self.with_tree_fields().extra(order_by=["-__tree.tree_depth"]).first()
|
|
48
58
|
if deepest is not None:
|
|
@@ -58,9 +68,21 @@ class TreeManager(TreeManager_, BaseManager.from_queryset(TreeQuerySet)):
|
|
|
58
68
|
_with_tree_fields = True
|
|
59
69
|
use_in_migrations = True
|
|
60
70
|
|
|
61
|
-
@
|
|
71
|
+
@property
|
|
72
|
+
def max_depth_cache_key(self):
|
|
73
|
+
return f"nautobot.{self.model._meta.concrete_model._meta.label_lower}.max_depth"
|
|
74
|
+
|
|
75
|
+
@property
|
|
62
76
|
def max_depth(self):
|
|
63
|
-
|
|
77
|
+
"""Cacheable version of `TreeQuerySet.max_tree_depth()`.
|
|
78
|
+
|
|
79
|
+
Generally TreeManagers are persistent objects while TreeQuerySets are not, hence the difference in behavior.
|
|
80
|
+
"""
|
|
81
|
+
max_depth = cache.get(self.max_depth_cache_key)
|
|
82
|
+
if max_depth is None:
|
|
83
|
+
max_depth = self.max_tree_depth()
|
|
84
|
+
cache.set(self.max_depth_cache_key, max_depth)
|
|
85
|
+
return max_depth
|
|
64
86
|
|
|
65
87
|
|
|
66
88
|
class TreeModel(TreeNode):
|
|
@@ -82,7 +104,7 @@ class TreeModel(TreeNode):
|
|
|
82
104
|
"""
|
|
83
105
|
if not hasattr(self, "name"):
|
|
84
106
|
raise NotImplementedError("default TreeModel.display implementation requires a `name` attribute!")
|
|
85
|
-
cache_key = f"{self.
|
|
107
|
+
cache_key = f"nautobot.{self._meta.concrete_model._meta.label_lower}.{self.id}.display"
|
|
86
108
|
display_str = cache.get(cache_key, "")
|
|
87
109
|
if display_str:
|
|
88
110
|
return display_str
|
nautobot/core/releases.py
CHANGED
|
@@ -18,7 +18,7 @@ def get_latest_release(pre_releases=False):
|
|
|
18
18
|
"""
|
|
19
19
|
if get_settings_or_config("RELEASE_CHECK_URL"):
|
|
20
20
|
logger.debug("Checking for most recent release")
|
|
21
|
-
latest_release = cache.get("
|
|
21
|
+
latest_release = cache.get("nautobot.core.releases.get_latest_release")
|
|
22
22
|
if latest_release is not None:
|
|
23
23
|
logger.debug(f"Found cached release: {latest_release}")
|
|
24
24
|
return latest_release
|
nautobot/core/settings.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import os.path
|
|
2
3
|
import platform
|
|
3
4
|
import re
|
|
4
5
|
import sys
|
|
6
|
+
import tempfile
|
|
5
7
|
|
|
6
8
|
from django.contrib.messages import constants as messages
|
|
7
9
|
import django.forms
|
|
@@ -240,6 +242,7 @@ SPECTACULAR_SETTINGS = {
|
|
|
240
242
|
# trim it from all of the individual paths correspondingly.
|
|
241
243
|
# See also https://github.com/nautobot/nautobot-ansible/pull/135 for an example of why this is desirable.
|
|
242
244
|
"SERVERS": [{"url": "/api"}],
|
|
245
|
+
"SERVE_PERMISSIONS": ["rest_framework.permissions.IsAuthenticated"],
|
|
243
246
|
"SCHEMA_PATH_PREFIX": "/api",
|
|
244
247
|
"SCHEMA_PATH_PREFIX_TRIM": True,
|
|
245
248
|
# use sidecar - locally packaged UI files, not CDN
|
|
@@ -767,6 +770,12 @@ CONTENT_TYPE_CACHE_TIMEOUT = int(os.getenv("NAUTOBOT_CONTENT_TYPE_CACHE_TIMEOUT"
|
|
|
767
770
|
# Celery (used for background processing)
|
|
768
771
|
#
|
|
769
772
|
|
|
773
|
+
# Celery Beat heartbeat file path - will be touched by Beat each time it wakes up as a proof-of-health.
|
|
774
|
+
CELERY_BEAT_HEARTBEAT_FILE = os.getenv(
|
|
775
|
+
"NAUTOBOT_CELERY_BEAT_HEARTBEAT_FILE",
|
|
776
|
+
os.path.join(tempfile.gettempdir(), "nautobot_celery_beat_heartbeat"),
|
|
777
|
+
)
|
|
778
|
+
|
|
770
779
|
# Celery broker URL used to tell workers where queues are located
|
|
771
780
|
CELERY_BROKER_URL = os.getenv("NAUTOBOT_CELERY_BROKER_URL", parse_redis_connection(redis_database=0))
|
|
772
781
|
|
nautobot/core/settings_funcs.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Helper functions to detect settings after app initialization (AKA 'dynamic settings')."""
|
|
2
2
|
|
|
3
3
|
from collections import namedtuple
|
|
4
|
-
from functools import lru_cache
|
|
5
4
|
import os
|
|
6
5
|
|
|
7
6
|
from django.conf import settings
|
|
@@ -10,26 +9,14 @@ ConstanceConfigItem = namedtuple("ConstanceConfigItem", ["default", "help_text",
|
|
|
10
9
|
|
|
11
10
|
#
|
|
12
11
|
# X_auth_enabled checks to see if a backend has been specified, thus assuming it is enabled.
|
|
13
|
-
# Leverages `lru_cache` since these are called per user session. The wrappers are a
|
|
14
|
-
# workaround to pass `lru_cache` a hashable data structure.
|
|
15
12
|
#
|
|
16
13
|
|
|
17
14
|
|
|
18
15
|
def remote_auth_enabled(auth_backends):
|
|
19
|
-
return _remote_auth_enabled(tuple(auth_backends))
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@lru_cache(maxsize=5)
|
|
23
|
-
def _remote_auth_enabled(auth_backends):
|
|
24
16
|
return "nautobot.core.authentication.RemoteUserBackend" in auth_backends
|
|
25
17
|
|
|
26
18
|
|
|
27
19
|
def sso_auth_enabled(auth_backends):
|
|
28
|
-
return _sso_auth_enabled(tuple(auth_backends))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@lru_cache(maxsize=5)
|
|
32
|
-
def _sso_auth_enabled(auth_backends):
|
|
33
20
|
for backend in auth_backends:
|
|
34
21
|
if backend.startswith(settings.SOCIAL_AUTH_BACKEND_PREFIX):
|
|
35
22
|
return True
|
|
@@ -37,11 +24,6 @@ def _sso_auth_enabled(auth_backends):
|
|
|
37
24
|
|
|
38
25
|
|
|
39
26
|
def ldap_auth_enabled(auth_backends):
|
|
40
|
-
return _ldap_auth_enabled(tuple(auth_backends))
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@lru_cache(maxsize=5)
|
|
44
|
-
def _ldap_auth_enabled(auth_backends):
|
|
45
27
|
return "django_auth_ldap.backend.LDAPBackend" in auth_backends
|
|
46
28
|
|
|
47
29
|
|
nautobot/core/signals.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""Custom signals and handlers for the core Nautobot application."""
|
|
2
|
+
import contextlib
|
|
2
3
|
from functools import wraps
|
|
3
4
|
import inspect
|
|
4
5
|
import logging
|
|
5
6
|
|
|
6
7
|
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
|
8
|
+
from django.core.cache import cache
|
|
7
9
|
from django.db.models.signals import post_delete, post_save
|
|
8
10
|
from django.dispatch import receiver, Signal
|
|
11
|
+
import redis.exceptions
|
|
9
12
|
|
|
10
13
|
nautobot_database_ready = Signal()
|
|
11
14
|
"""
|
|
@@ -67,8 +70,5 @@ def invalidate_max_depth_cache(sender, **kwargs):
|
|
|
67
70
|
if not isinstance(sender.objects, TreeManager):
|
|
68
71
|
return
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
del sender.objects.max_depth
|
|
73
|
-
except AttributeError:
|
|
74
|
-
pass
|
|
73
|
+
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
74
|
+
cache.delete(sender.objects.max_depth_cache_key)
|
nautobot/core/tasks.py
CHANGED
|
@@ -21,7 +21,7 @@ def get_releases(pre_releases=False):
|
|
|
21
21
|
releases = []
|
|
22
22
|
|
|
23
23
|
# Check whether this URL has failed recently and shouldn't be retried yet
|
|
24
|
-
if url == cache.get("
|
|
24
|
+
if url == cache.get("nautobot.core.releases.get_releases.no_retry"):
|
|
25
25
|
logger.info(f"Skipping release check; URL failed recently: {url}")
|
|
26
26
|
return []
|
|
27
27
|
|
|
@@ -42,11 +42,15 @@ def get_releases(pre_releases=False):
|
|
|
42
42
|
except requests.exceptions.RequestException:
|
|
43
43
|
# The request failed. Set a flag in the cache to disable future checks to this URL for 15 minutes.
|
|
44
44
|
logger.exception(f"Error while fetching {url}. Disabling checks for 15 minutes.")
|
|
45
|
-
cache.set("
|
|
45
|
+
cache.set("nautobot.core.releases.get_releases.no_retry", url, 900)
|
|
46
46
|
return []
|
|
47
47
|
|
|
48
48
|
# Cache the most recent release
|
|
49
|
-
cache.set(
|
|
49
|
+
cache.set(
|
|
50
|
+
"nautobot.core.releases.get_latest_release",
|
|
51
|
+
max(releases),
|
|
52
|
+
config.get_settings_or_config("RELEASE_CHECK_TIMEOUT"),
|
|
53
|
+
)
|
|
50
54
|
|
|
51
55
|
# Since this is a Celery task, we can't return Version objects as they are not JSON serializable.
|
|
52
56
|
return [(str(version), url) for version, url in releases]
|
|
@@ -8,55 +8,7 @@
|
|
|
8
8
|
<html lang="en"{% if request.COOKIES|get_item:"theme" == 'dark' %} data-theme="dark"{% endif %}>
|
|
9
9
|
<head>
|
|
10
10
|
<title>{% block title %}Admin Home{% endblock %} - Nautobot</title>
|
|
11
|
-
|
|
12
|
-
href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}"
|
|
13
|
-
onerror="window.location='{% url 'media_failure' %}?filename=bootstrap-3.4.1-dist/css/bootstrap.min.css'">
|
|
14
|
-
<link rel="stylesheet"
|
|
15
|
-
href="{% static 'materialdesignicons-6.5.95/css/materialdesignicons.min.css' %}"
|
|
16
|
-
onerror="window.location='{% url 'media_failure' %}?filename=materialdesignicons-6.5.95/css/materialdesignicons.min.css'">
|
|
17
|
-
<link rel="stylesheet"
|
|
18
|
-
href="{% static 'jquery-ui-1.13.1/jquery-ui.min.css' %}"
|
|
19
|
-
onerror="window.location='{% url 'media_failure' %}?filename=jquery-ui-1.13.1/jquery-ui.min.css'">
|
|
20
|
-
<link rel="stylesheet"
|
|
21
|
-
href="{% static 'select2-4.0.13/select2.min.css' %}"
|
|
22
|
-
onerror="window.location='{% url 'media_failure' %}?filename=select2-4.0.13/select2.min.css'">
|
|
23
|
-
<link rel="stylesheet"
|
|
24
|
-
href="{% static 'select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css' %}"
|
|
25
|
-
onerror="window.location='{% url 'media_failure' %}?filename=select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css'">
|
|
26
|
-
<link rel="stylesheet"
|
|
27
|
-
href="{% static 'flatpickr-4.6.9/themes/light.min.css' %}"
|
|
28
|
-
onerror="window.location='{% url 'media_failure' %}?filename=flatpickr-4.6.9/themes/light.min.css'">
|
|
29
|
-
<link rel="stylesheet" id="dark-theme"
|
|
30
|
-
href="{% versioned_static 'css/dark.css' %}"
|
|
31
|
-
onerror="window.location='{% url 'media_failure' %}?filename=css/dark.css'"{% if request.COOKIES|get_item:"theme" != 'dark' %} disabled="disabled"{% endif %} />
|
|
32
|
-
<link rel="stylesheet" id="base-theme"
|
|
33
|
-
href="{% versioned_static 'css/base.css' %}"
|
|
34
|
-
onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'">
|
|
35
|
-
<link rel="apple-touch-icon" sizes="180x180" href="{% custom_branding_or_static 'icon_180' 'img/nautobot_icon_180x180.png' %}">
|
|
36
|
-
<link rel="icon" type="image/png" sizes="32x32" href="{% custom_branding_or_static 'icon_32' 'img/nautobot_icon_32x32.png' %}">
|
|
37
|
-
<link rel="icon" type="image/png" sizes="16x16" href="{% custom_branding_or_static 'icon_16' 'img/nautobot_icon_16x16.png' %}">
|
|
38
|
-
<link rel="icon" type="image/png" href="{% custom_branding_or_static 'icon_192' 'img/nautobot_icon_192x192.png' %}" sizes="192x192">
|
|
39
|
-
<link rel="mask-icon" href="{% custom_branding_or_static 'icon_mask' 'img/nautobot_icon_monochrome.svg' %}" color="#0097ff">
|
|
40
|
-
<link rel="shortcut icon" href="{% custom_branding_or_static 'favicon' 'img/favicon.ico' %}">
|
|
41
|
-
|
|
42
|
-
<script src="{% static 'jquery/jquery-3.6.0.min.js' %}"
|
|
43
|
-
onerror="window.location='{% url 'media_failure' %}?filename=jquery/jquery-3.6.0.min.js'"></script>
|
|
44
|
-
<script src="{% static 'jquery-ui-1.13.1/jquery-ui.min.js' %}"
|
|
45
|
-
onerror="window.location='{% url 'media_failure' %}?filename=jquery-ui-1.13.1/jquery-ui.min.js'"></script>
|
|
46
|
-
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"
|
|
47
|
-
onerror="window.location='{% url 'media_failure' %}?filename=bootstrap-3.4.1-dist/js/bootstrap.min.js'"></script>
|
|
48
|
-
<script src="{% static 'select2-4.0.13/select2.min.js' %}"
|
|
49
|
-
onerror="window.location='{% url 'media_failure' %}?filename=select2-4.0.13/select2.min.js'"></script>
|
|
50
|
-
<script src="{% static 'flatpickr-4.6.9/flatpickr.min.js' %}"
|
|
51
|
-
onerror="window.location='{% url 'media_failure' %}?filename=flatpickr-4.6.9/flatpickr.min.js'"></script>
|
|
52
|
-
<script src="{% static 'js/forms.js' %}"
|
|
53
|
-
onerror="window.location='{% url 'media_failure' %}?filename=js/forms.js'"></script>
|
|
54
|
-
|
|
55
|
-
<meta name="msapplication-TileColor" content="#2d89ef">
|
|
56
|
-
<meta name="theme-color" content="#ffffff">
|
|
57
|
-
<meta charset="UTF-8">
|
|
58
|
-
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
|
59
|
-
|
|
11
|
+
{% include 'inc/media.html' %}
|
|
60
12
|
{% block extrahead %}{% endblock %}
|
|
61
13
|
{% block extrastyle %}{% endblock %}
|
|
62
14
|
|
|
@@ -94,23 +46,27 @@
|
|
|
94
46
|
}
|
|
95
47
|
</style>
|
|
96
48
|
</head>
|
|
97
|
-
<body {% if is_popup %}style="padding-
|
|
49
|
+
<body {% if is_popup %}style="padding-left: 0px;"{% endif %}>
|
|
50
|
+
|
|
98
51
|
{% if not is_popup %}
|
|
99
52
|
{% include 'inc/nav_menu.html' %}
|
|
100
53
|
{% endif %}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<
|
|
111
|
-
|
|
54
|
+
|
|
55
|
+
<div class="container-fluid wrapper" id="main-content" {% if is_popup %}style="padding-bottom: 0px;"{% endif %}>
|
|
56
|
+
{% if not is_popup %}
|
|
57
|
+
{% if "BANNER_TOP"|settings_or_config %}
|
|
58
|
+
<div class="alert alert-info text-center" role="alert">
|
|
59
|
+
{{ "BANNER_TOP"|settings_or_config|safe }}
|
|
60
|
+
</div>
|
|
61
|
+
{% endif %}
|
|
62
|
+
{% if settings.MAINTENANCE_MODE %}
|
|
63
|
+
<div class="alert alert-warning text-center" role="alert">
|
|
64
|
+
<h4><i class="mdi mdi-alert"></i> Maintenance Mode</h4>
|
|
65
|
+
<p>Nautobot is currently in maintenance mode. Functionality may be limited.</p>
|
|
66
|
+
</div>
|
|
67
|
+
{% endif %}
|
|
68
|
+
{% plugin_banners %}
|
|
112
69
|
{% endif %}
|
|
113
|
-
{% plugin_banners %}
|
|
114
70
|
{% for message in messages %}
|
|
115
71
|
<div class="alert alert-{{ message.tags }} alert-dismissable" role="alert">
|
|
116
72
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
@@ -148,8 +104,6 @@
|
|
|
148
104
|
{% block content %}{{ content }}{% endblock %}
|
|
149
105
|
</div>
|
|
150
106
|
</div>
|
|
151
|
-
|
|
152
|
-
{% block footer %}<footer id="footer"></footer>{% endblock %}
|
|
153
107
|
<div class="push"></div>
|
|
154
108
|
{% if "BANNER_BOTTOM"|settings_or_config %}
|
|
155
109
|
<div class="alert alert-info text-center banner-bottom" role="alert">
|
|
@@ -158,37 +112,12 @@
|
|
|
158
112
|
{% endif %}
|
|
159
113
|
</div>
|
|
160
114
|
{% if not is_popup %}
|
|
161
|
-
|
|
162
|
-
<div class="container-fluid">
|
|
163
|
-
<div class="row">
|
|
164
|
-
<div class="col-xs-4">
|
|
165
|
-
<p class="text-muted">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</p>
|
|
166
|
-
</div>
|
|
167
|
-
<div class="col-xs-4 text-center">
|
|
168
|
-
<p class="text-muted">{% now 'Y-m-d H:i:s T' %}</p>
|
|
169
|
-
</div>
|
|
170
|
-
<div class="col-xs-4 text-right noprint">
|
|
171
|
-
<p class="text-muted">
|
|
172
|
-
<a href="#theme_modal" data-toggle="modal" data-target="#theme_modal" id="btn-theme-modal"><i class="mdi mdi-theme-light-dark text-primary"></i>Theme</a> ·
|
|
173
|
-
<i class="mdi mdi-book-open-page-variant text-primary"></i>
|
|
174
|
-
{% if settings.BRANDING_URLS.docs %}
|
|
175
|
-
<a href="{{ settings.BRANDING_URLS.docs }}">Docs</a>
|
|
176
|
-
{% else %}
|
|
177
|
-
<a href="{% static 'docs/index.html' %}">Docs</a>
|
|
178
|
-
{% endif %}
|
|
179
|
-
·
|
|
180
|
-
<i class="mdi mdi-cloud-braces text-primary"></i> <a href="{% url 'api_docs' %}">API</a> ·
|
|
181
|
-
<i class="mdi mdi-graphql text-primary"></i> <a href="{% url 'graphql' %}">GraphQL</a> ·
|
|
182
|
-
<i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}">Code</a> ·
|
|
183
|
-
<i class="mdi mdi-lifebuoy text-primary"></i> <a href="{{ settings.BRANDING_URLS.help }}">Help</a>
|
|
184
|
-
</p>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
</footer>
|
|
115
|
+
{% include 'inc/footer.html' %}
|
|
189
116
|
{% endif %}
|
|
190
117
|
{% include 'modals/modal_theme.html' with name='theme'%}
|
|
191
|
-
|
|
192
|
-
{% block javascript %}
|
|
118
|
+
|
|
119
|
+
{% block javascript %}
|
|
120
|
+
{% include 'inc/javascript.html' %}
|
|
121
|
+
{% endblock %}
|
|
193
122
|
</body>
|
|
194
123
|
</html>
|