nautobot 2.4.9__py3-none-any.whl → 2.4.11__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/cloud/tests/test_views.py +13 -1
- nautobot/cloud/views.py +39 -9
- nautobot/core/api/parsers.py +56 -2
- nautobot/core/celery/__init__.py +21 -0
- nautobot/core/celery/encoders.py +3 -0
- nautobot/core/forms/forms.py +4 -1
- nautobot/core/jobs/bulk_actions.py +8 -8
- nautobot/core/jobs/cleanup.py +11 -0
- nautobot/core/management/commands/generate_test_data.py +2 -1
- nautobot/core/models/__init__.py +2 -0
- nautobot/core/templates/generic/object_retrieve.html +1 -1
- nautobot/core/testing/mixins.py +19 -1
- nautobot/core/testing/views.py +104 -8
- nautobot/core/tests/test_csv.py +92 -1
- nautobot/core/tests/test_jinja_filters.py +59 -0
- nautobot/core/tests/test_jobs.py +20 -4
- nautobot/core/tests/test_utils.py +193 -0
- nautobot/core/tests/test_views.py +73 -0
- nautobot/core/tests/test_views_utils.py +53 -2
- nautobot/core/ui/object_detail.py +4 -0
- nautobot/core/urls.py +2 -2
- nautobot/core/utils/lookup.py +4 -2
- nautobot/core/utils/module_loading.py +86 -58
- nautobot/core/views/__init__.py +21 -0
- nautobot/core/views/generic.py +2 -12
- nautobot/core/views/mixins.py +19 -1
- nautobot/core/views/renderers.py +4 -13
- nautobot/core/views/utils.py +16 -0
- nautobot/dcim/api/serializers.py +13 -0
- nautobot/dcim/api/urls.py +1 -0
- nautobot/dcim/api/views.py +20 -0
- nautobot/dcim/apps.py +1 -0
- nautobot/dcim/factory.py +11 -0
- nautobot/dcim/filters/__init__.py +110 -0
- nautobot/dcim/forms.py +205 -19
- nautobot/dcim/migrations/0070_modulefamily_models.py +92 -0
- nautobot/dcim/models/__init__.py +2 -0
- nautobot/dcim/models/device_component_templates.py +18 -0
- nautobot/dcim/models/device_components.py +25 -1
- nautobot/dcim/models/devices.py +68 -0
- nautobot/dcim/navigation.py +16 -0
- nautobot/dcim/tables/__init__.py +2 -0
- nautobot/dcim/tables/devices.py +48 -0
- nautobot/dcim/tables/devicetypes.py +35 -1
- nautobot/dcim/tables/template_code.py +2 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -90
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +1 -63
- nautobot/dcim/templates/dcim/location.html +2 -249
- nautobot/dcim/templates/dcim/location_edit.html +2 -38
- nautobot/dcim/templates/dcim/location_retrieve.html +249 -0
- nautobot/dcim/templates/dcim/location_update.html +38 -0
- nautobot/dcim/templates/dcim/module_update.html +1 -0
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +93 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +31 -0
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +6 -0
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -160
- nautobot/dcim/tests/test_api.py +35 -0
- nautobot/dcim/tests/test_filters.py +102 -3
- nautobot/dcim/tests/test_models.py +146 -0
- nautobot/dcim/tests/test_views.py +70 -97
- nautobot/dcim/urls.py +4 -22
- nautobot/dcim/views.py +439 -153
- nautobot/extras/api/views.py +9 -2
- nautobot/extras/context_managers.py +2 -2
- nautobot/extras/datasources/git.py +11 -3
- nautobot/extras/forms/forms.py +9 -5
- nautobot/extras/jobs.py +4 -2
- nautobot/extras/models/customfields.py +2 -0
- nautobot/extras/models/datasources.py +13 -8
- nautobot/extras/models/groups.py +18 -0
- nautobot/extras/models/jobs.py +19 -0
- nautobot/extras/models/metadata.py +2 -0
- nautobot/extras/models/models.py +4 -0
- nautobot/extras/models/secrets.py +7 -0
- nautobot/extras/plugins/__init__.py +3 -0
- nautobot/extras/secrets/__init__.py +14 -0
- nautobot/extras/tables.py +40 -3
- nautobot/extras/templates/extras/configcontext.html +2 -220
- nautobot/extras/templates/extras/configcontext_edit.html +2 -50
- nautobot/extras/templates/extras/configcontext_retrieve.html +2 -0
- nautobot/extras/templates/extras/configcontext_update.html +50 -0
- nautobot/extras/templates/extras/configcontextschema.html +2 -48
- nautobot/extras/templates/extras/configcontextschema_edit.html +2 -19
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +48 -0
- nautobot/extras/templates/extras/configcontextschema_update.html +19 -0
- nautobot/extras/templates/extras/inc/configcontext_data.html +1 -0
- nautobot/extras/templates/extras/inc/json_data.html +1 -1
- nautobot/extras/templates/extras/inc/json_format.html +2 -2
- nautobot/extras/templates/extras/job_edit.html +12 -6
- nautobot/extras/templates/extras/tag.html +2 -52
- nautobot/extras/templates/extras/tag_edit.html +2 -15
- nautobot/extras/templates/extras/tag_retrieve.html +52 -0
- nautobot/extras/templates/extras/tag_update.html +15 -0
- nautobot/extras/templates/extras/team_retrieve.html +2 -2
- nautobot/extras/tests/test_api.py +15 -15
- nautobot/extras/tests/test_context_managers.py +20 -0
- nautobot/extras/tests/test_filters.py +4 -4
- nautobot/extras/tests/test_jobs.py +23 -10
- nautobot/extras/tests/test_models.py +45 -8
- nautobot/extras/tests/test_plugins.py +6 -3
- nautobot/extras/tests/test_views.py +66 -11
- nautobot/extras/urls.py +4 -134
- nautobot/extras/views.py +113 -158
- nautobot/ipam/models.py +51 -4
- nautobot/ipam/tables.py +19 -0
- nautobot/ipam/templates/ipam/vlan.html +2 -84
- nautobot/ipam/templates/ipam/vlan_edit.html +2 -24
- nautobot/ipam/templates/ipam/vlan_retrieve.html +84 -0
- nautobot/ipam/templates/ipam/vlan_update.html +24 -0
- nautobot/ipam/tests/test_views.py +5 -0
- nautobot/ipam/urls.py +1 -21
- nautobot/ipam/views.py +45 -70
- nautobot/project-static/docs/404.html +31 -8
- nautobot/project-static/docs/apps/index.html +31 -8
- nautobot/project-static/docs/apps/nautobot-apps.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +120 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +31 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +31 -8
- nautobot/project-static/docs/development/apps/api/configuration-view.html +31 -8
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +31 -8
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +31 -8
- nautobot/project-static/docs/development/apps/api/models/global-search.html +31 -8
- nautobot/project-static/docs/development/apps/api/models/graphql.html +31 -8
- nautobot/project-static/docs/development/apps/api/models/index.html +31 -8
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +40 -8
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +70 -46
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +31 -8
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +31 -8
- nautobot/project-static/docs/development/apps/api/prometheus.html +31 -8
- nautobot/project-static/docs/development/apps/api/setup.html +31 -8
- nautobot/project-static/docs/development/apps/api/testing.html +31 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +31 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +31 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +31 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +31 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/base-template.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/index.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/notes.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +31 -8
- nautobot/project-static/docs/development/apps/api/views/urls.html +31 -8
- nautobot/project-static/docs/development/apps/index.html +31 -8
- nautobot/project-static/docs/development/apps/migration/code-updates.html +31 -8
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +31 -8
- nautobot/project-static/docs/development/apps/migration/from-v1.html +31 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +31 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +31 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +31 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +31 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +31 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +31 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +31 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +31 -8
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +31 -8
- nautobot/project-static/docs/development/core/application-registry.html +31 -8
- nautobot/project-static/docs/development/core/best-practices.html +31 -8
- nautobot/project-static/docs/development/core/bootstrap-ui.html +31 -8
- nautobot/project-static/docs/development/core/caching.html +31 -8
- nautobot/project-static/docs/development/core/controllers.html +31 -8
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +31 -8
- nautobot/project-static/docs/development/core/generic-views.html +31 -8
- nautobot/project-static/docs/development/core/getting-started.html +31 -8
- nautobot/project-static/docs/development/core/homepage.html +31 -8
- nautobot/project-static/docs/development/core/index.html +31 -8
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +31 -8
- nautobot/project-static/docs/development/core/model-checklist.html +31 -8
- nautobot/project-static/docs/development/core/model-features.html +31 -8
- nautobot/project-static/docs/development/core/natural-keys.html +31 -8
- nautobot/project-static/docs/development/core/navigation-menu.html +31 -8
- nautobot/project-static/docs/development/core/release-checklist.html +31 -8
- nautobot/project-static/docs/development/core/role-internals.html +31 -8
- nautobot/project-static/docs/development/core/settings.html +31 -8
- nautobot/project-static/docs/development/core/style-guide.html +31 -8
- nautobot/project-static/docs/development/core/templates.html +31 -8
- nautobot/project-static/docs/development/core/testing.html +31 -8
- nautobot/project-static/docs/development/core/ui-component-framework.html +31 -8
- nautobot/project-static/docs/development/core/user-preferences.html +31 -8
- nautobot/project-static/docs/development/index.html +31 -8
- nautobot/project-static/docs/development/jobs/getting-started.html +35 -8
- nautobot/project-static/docs/development/jobs/index.html +31 -8
- nautobot/project-static/docs/development/jobs/installation.html +31 -8
- nautobot/project-static/docs/development/jobs/job-extensions.html +31 -8
- nautobot/project-static/docs/development/jobs/job-logging.html +31 -8
- nautobot/project-static/docs/development/jobs/job-patterns.html +31 -8
- nautobot/project-static/docs/development/jobs/job-structure.html +31 -8
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +31 -8
- nautobot/project-static/docs/development/jobs/testing.html +31 -8
- nautobot/project-static/docs/index.html +31 -8
- nautobot/project-static/docs/insert-analytics.sh +36 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +31 -8
- nautobot/project-static/docs/overview/design_philosophy.html +31 -8
- nautobot/project-static/docs/release-notes/index.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.0.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.1.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.2.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.3.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.4.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.5.html +31 -8
- nautobot/project-static/docs/release-notes/version-1.6.html +328 -8
- nautobot/project-static/docs/release-notes/version-2.0.html +31 -8
- nautobot/project-static/docs/release-notes/version-2.1.html +31 -8
- nautobot/project-static/docs/release-notes/version-2.2.html +31 -8
- nautobot/project-static/docs/release-notes/version-2.3.html +31 -8
- nautobot/project-static/docs/release-notes/version-2.4.html +353 -8
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +302 -298
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +31 -8
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +31 -8
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +31 -8
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +31 -8
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +31 -8
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +31 -8
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +31 -8
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/index.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +31 -8
- nautobot/project-static/docs/user-guide/administration/installation/services.html +31 -8
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +31 -8
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +31 -8
- nautobot/project-static/docs/user-guide/administration/security/index.html +31 -9
- nautobot/project-static/docs/user-guide/administration/security/notices.html +144 -9
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +31 -8
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +31 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +43 -20
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +35 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +35 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +35 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +10261 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +34 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +31 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +41 -15
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +31 -8
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +31 -8
- nautobot/project-static/docs/user-guide/index.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +37 -9
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +31 -8
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +31 -8
- nautobot/tenancy/tables.py +2 -0
- nautobot/users/models.py +4 -0
- nautobot/virtualization/models.py +4 -0
- nautobot/virtualization/tests/test_views.py +1 -1
- nautobot/wireless/forms.py +0 -1
- nautobot/wireless/models.py +1 -1
- nautobot/wireless/tables.py +7 -0
- {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/METADATA +4 -4
- {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/RECORD +433 -418
- /nautobot/dcim/templates/dcim/{platform_edit.html → platform_create.html} +0 -0
- /nautobot/extras/test_jobs/{pass.py → pass_job.py} +0 -0
- {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/NOTICE +0 -0
- {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/WHEEL +0 -0
- {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/entry_points.txt +0 -0
nautobot/extras/api/views.py
CHANGED
|
@@ -512,12 +512,19 @@ class JobViewSetBase(
|
|
|
512
512
|
serializer_class = serializers.JobSerializer
|
|
513
513
|
filterset_class = filters.JobFilterSet
|
|
514
514
|
|
|
515
|
+
def get_object(self):
|
|
516
|
+
"""Get the Job instance and reload the job class to ensure we have the latest version of the job code."""
|
|
517
|
+
obj = super().get_object()
|
|
518
|
+
get_job(obj.class_path, reload=True)
|
|
519
|
+
|
|
520
|
+
return obj
|
|
521
|
+
|
|
515
522
|
@extend_schema(responses={"200": serializers.JobVariableSerializer(many=True)})
|
|
516
523
|
@action(detail=True, filterset_class=None)
|
|
517
524
|
def variables(self, request, *args, **kwargs):
|
|
518
525
|
"""Get details of the input variables that may/must be specified to run a particular Job."""
|
|
519
526
|
job_model = self.get_object()
|
|
520
|
-
job_class =
|
|
527
|
+
job_class = job_model.job_class
|
|
521
528
|
if job_class is None:
|
|
522
529
|
raise Http404
|
|
523
530
|
variables_dict = job_class._get_vars()
|
|
@@ -599,7 +606,7 @@ class JobViewSetBase(
|
|
|
599
606
|
"One of these two flags must be removed before this job can be scheduled or run."
|
|
600
607
|
)
|
|
601
608
|
|
|
602
|
-
job_class =
|
|
609
|
+
job_class = job_model.job_class
|
|
603
610
|
if job_class is None:
|
|
604
611
|
raise MethodNotAllowed(
|
|
605
612
|
request.method, detail="This job's source code could not be located and cannot be run"
|
|
@@ -254,8 +254,8 @@ def web_request_context(
|
|
|
254
254
|
# TODO: get_snapshots() currently requires a DB query per object change processed.
|
|
255
255
|
# We need to develop a more efficient approach: https://github.com/nautobot/nautobot/issues/6303
|
|
256
256
|
snapshots = oc.get_snapshots(
|
|
257
|
-
pre_object_data.get(str(oc.changed_object_id), None),
|
|
258
|
-
pre_object_data_v2.get(str(oc.changed_object_id), None),
|
|
257
|
+
pre_object_data.get(str(oc.changed_object_id), None) if pre_object_data else None,
|
|
258
|
+
pre_object_data_v2.get(str(oc.changed_object_id), None) if pre_object_data_v2 else None,
|
|
259
259
|
)
|
|
260
260
|
webhook_queryset = enqueue_webhooks(oc, snapshots=snapshots, webhook_queryset=webhook_queryset)
|
|
261
261
|
|
|
@@ -17,7 +17,7 @@ from git import InvalidGitRepositoryError, Repo
|
|
|
17
17
|
import yaml
|
|
18
18
|
|
|
19
19
|
from nautobot.core.utils.git import GitRepo
|
|
20
|
-
from nautobot.core.utils.module_loading import import_modules_privately
|
|
20
|
+
from nautobot.core.utils.module_loading import check_name_safe_to_import_privately, import_modules_privately
|
|
21
21
|
from nautobot.dcim.models import Device, DeviceRedundancyGroup, DeviceType, Location, Platform
|
|
22
22
|
from nautobot.extras.choices import (
|
|
23
23
|
LogLevelChoices,
|
|
@@ -720,11 +720,19 @@ def refresh_job_code_from_repository(repository_slug, skip_reimport=False, ignor
|
|
|
720
720
|
After cloning/updating/deleting a GitRepository on disk, call this function to reload and reregister its Python.
|
|
721
721
|
|
|
722
722
|
Args:
|
|
723
|
-
repository_slug (str): Repository directory in GIT_ROOT that was updated or deleted.
|
|
724
|
-
skip_reimport (bool): If True, unload existing
|
|
723
|
+
repository_slug (str): Repository slug (directory in GIT_ROOT) that was populated, updated, or deleted.
|
|
724
|
+
skip_reimport (bool): If True, unload existing jobs from this repository but do not re-import them.
|
|
725
725
|
ignore_import_errors (bool): If True, any exceptions raised in the import will be caught and logged.
|
|
726
726
|
If False, exceptions will be re-raised after logging.
|
|
727
727
|
"""
|
|
728
|
+
# Enforced during GitRepository.clean() but just in case someone created a bad record without cleaning:
|
|
729
|
+
permitted, reason = check_name_safe_to_import_privately(repository_slug)
|
|
730
|
+
if not permitted:
|
|
731
|
+
logger.error("The repository_slug %r is invalid as it is %s", repository_slug, reason)
|
|
732
|
+
if ignore_import_errors:
|
|
733
|
+
return
|
|
734
|
+
raise ValueError(f"The repository_slug {repository_slug!r} is invalid as it is {reason}")
|
|
735
|
+
|
|
728
736
|
# Unload any previous version of this module and its submodules if present
|
|
729
737
|
for job_class_path in list(registry["jobs"]):
|
|
730
738
|
if job_class_path.startswith(f"{repository_slug}."):
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -914,13 +914,13 @@ class ExternalIntegrationBulkEditForm(NautobotBulkEditForm):
|
|
|
914
914
|
secrets_group = DynamicModelChoiceField(required=False, queryset=SecretsGroup.objects.all())
|
|
915
915
|
verify_ssl = forms.NullBooleanField(required=False, label="Verify SSL", widget=BulkEditNullBooleanSelect)
|
|
916
916
|
timeout = forms.IntegerField(required=False, min_value=0)
|
|
917
|
-
extra_config =
|
|
917
|
+
extra_config = JSONField(required=False, widget=forms.Textarea, help_text="JSON data")
|
|
918
918
|
http_method = forms.ChoiceField(
|
|
919
919
|
required=False,
|
|
920
920
|
label="HTTP Method",
|
|
921
921
|
choices=add_blank_choice(WebhookHttpMethodChoices),
|
|
922
922
|
)
|
|
923
|
-
headers =
|
|
923
|
+
headers = JSONField(required=False, widget=forms.Textarea, help_text="Headers for the HTTP request")
|
|
924
924
|
|
|
925
925
|
class Meta:
|
|
926
926
|
model = ExternalIntegration
|
|
@@ -1487,7 +1487,6 @@ class JobQueueBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
|
1487
1487
|
class Meta:
|
|
1488
1488
|
model = JobQueue
|
|
1489
1489
|
nullable_fields = [
|
|
1490
|
-
"secrets_group",
|
|
1491
1490
|
"description",
|
|
1492
1491
|
"tenant",
|
|
1493
1492
|
]
|
|
@@ -1910,8 +1909,8 @@ class RelationshipBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditFormMixin
|
|
|
1910
1909
|
)
|
|
1911
1910
|
source_hidden = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
|
|
1912
1911
|
destination_hidden = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
|
|
1913
|
-
source_filter =
|
|
1914
|
-
destination_filter =
|
|
1912
|
+
source_filter = JSONField(required=False, widget=forms.Textarea, help_text="Filter for the source")
|
|
1913
|
+
destination_filter = JSONField(required=False, widget=forms.Textarea, help_text="Filter for the destination")
|
|
1915
1914
|
source_type = CSVContentTypeField(
|
|
1916
1915
|
queryset=ContentType.objects.filter(FeatureQuery("relationships").get_query()), required=False
|
|
1917
1916
|
)
|
|
@@ -2256,6 +2255,8 @@ class WebhookBulkEditForm(BootstrapMixin, NoteModelBulkEditFormMixin):
|
|
|
2256
2255
|
secret = forms.CharField(required=False, max_length=CHARFIELD_MAX_LENGTH)
|
|
2257
2256
|
ca_file_path = forms.CharField(required=False, max_length=4096)
|
|
2258
2257
|
http_content_type = forms.CharField(required=False, max_length=CHARFIELD_MAX_LENGTH)
|
|
2258
|
+
additional_headers = forms.CharField(required=False, widget=forms.Textarea)
|
|
2259
|
+
body_template = forms.CharField(required=False, widget=forms.Textarea)
|
|
2259
2260
|
|
|
2260
2261
|
# Choice field
|
|
2261
2262
|
http_method = forms.ChoiceField(
|
|
@@ -2279,6 +2280,8 @@ class WebhookBulkEditForm(BootstrapMixin, NoteModelBulkEditFormMixin):
|
|
|
2279
2280
|
"type_delete",
|
|
2280
2281
|
"http_method",
|
|
2281
2282
|
"http_content_type",
|
|
2283
|
+
"additional_headers",
|
|
2284
|
+
"body_template",
|
|
2282
2285
|
"ssl_verification",
|
|
2283
2286
|
"ca_file_path",
|
|
2284
2287
|
"payload_url",
|
|
@@ -2286,6 +2289,7 @@ class WebhookBulkEditForm(BootstrapMixin, NoteModelBulkEditFormMixin):
|
|
|
2286
2289
|
"add_content_types",
|
|
2287
2290
|
"remove_content_types",
|
|
2288
2291
|
)
|
|
2292
|
+
nullable_fields = ("additional_headers",)
|
|
2289
2293
|
|
|
2290
2294
|
|
|
2291
2295
|
class WebhookForm(BootstrapMixin, forms.ModelForm):
|
nautobot/extras/jobs.py
CHANGED
|
@@ -17,6 +17,7 @@ from celery.exceptions import Ignore, Reject
|
|
|
17
17
|
from celery.utils.log import get_task_logger
|
|
18
18
|
from db_file_storage.form_widgets import DBClearableFileInput
|
|
19
19
|
from django import forms
|
|
20
|
+
from django.apps import apps
|
|
20
21
|
from django.conf import settings
|
|
21
22
|
from django.contrib.auth import get_user_model
|
|
22
23
|
from django.core.cache import cache
|
|
@@ -1142,8 +1143,9 @@ def get_job(class_path, reload=False):
|
|
|
1142
1143
|
# System job - not reloadable
|
|
1143
1144
|
reload = False
|
|
1144
1145
|
if any(class_path.startswith(f"{app_name}.") for app_name in settings.PLUGINS):
|
|
1145
|
-
# App provided job -
|
|
1146
|
-
|
|
1146
|
+
# App provided job - only reload if the app provides dynamic jobs
|
|
1147
|
+
app_config = apps.get_app_config(class_path.split(".")[0])
|
|
1148
|
+
reload = getattr(app_config, "provides_dynamic_jobs", False)
|
|
1147
1149
|
jobs = get_jobs(reload=reload)
|
|
1148
1150
|
return jobs.get(class_path, None)
|
|
1149
1151
|
|
|
@@ -269,6 +269,8 @@ class CustomFieldModel(models.Model):
|
|
|
269
269
|
elif cf.required:
|
|
270
270
|
raise ValidationError(f"Missing required custom field '{cf.key}'.")
|
|
271
271
|
|
|
272
|
+
clean.alters_data = True
|
|
273
|
+
|
|
272
274
|
# Computed Field Methods
|
|
273
275
|
def has_computed_fields(self, advanced_ui=None):
|
|
274
276
|
"""
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Models for representing external data sources."""
|
|
2
2
|
|
|
3
3
|
from contextlib import contextmanager
|
|
4
|
-
from importlib.util import find_spec
|
|
5
4
|
import logging
|
|
6
5
|
import os
|
|
7
6
|
import shutil
|
|
@@ -17,7 +16,8 @@ from nautobot.core.models.fields import AutoSlugField, LaxURLField, slugify_dash
|
|
|
17
16
|
from nautobot.core.models.generics import PrimaryModel
|
|
18
17
|
from nautobot.core.models.validators import EnhancedURLValidator
|
|
19
18
|
from nautobot.core.utils.git import GitRepo
|
|
20
|
-
from nautobot.
|
|
19
|
+
from nautobot.core.utils.module_loading import check_name_safe_to_import_privately
|
|
20
|
+
from nautobot.extras.utils import extras_features
|
|
21
21
|
|
|
22
22
|
logger = logging.getLogger(__name__)
|
|
23
23
|
|
|
@@ -105,12 +105,9 @@ class GitRepository(PrimaryModel):
|
|
|
105
105
|
)
|
|
106
106
|
|
|
107
107
|
if not self.present_in_database:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
raise ValidationError(
|
|
112
|
-
f'Please choose a different slug, as "{self.slug}" is an installed Python package or module.'
|
|
113
|
-
)
|
|
108
|
+
permitted, reason = check_name_safe_to_import_privately(self.slug)
|
|
109
|
+
if not permitted:
|
|
110
|
+
raise ValidationError({"slug": f"Please choose a different slug; {self.slug!r} is {reason}"})
|
|
114
111
|
|
|
115
112
|
if self.provided_contents:
|
|
116
113
|
q = models.Q()
|
|
@@ -180,6 +177,8 @@ class GitRepository(PrimaryModel):
|
|
|
180
177
|
return enqueue_git_repository_diff_origin_and_local(self, user)
|
|
181
178
|
return enqueue_pull_git_repository_and_refresh_data(self, user)
|
|
182
179
|
|
|
180
|
+
sync.alters_data = True
|
|
181
|
+
|
|
183
182
|
@contextmanager
|
|
184
183
|
def clone_to_directory_context(self, path=None, branch=None, head=None, depth=0):
|
|
185
184
|
"""
|
|
@@ -207,6 +206,8 @@ class GitRepository(PrimaryModel):
|
|
|
207
206
|
if path_name:
|
|
208
207
|
self.cleanup_cloned_directory(path_name)
|
|
209
208
|
|
|
209
|
+
clone_to_directory_context.alters_data = True
|
|
210
|
+
|
|
210
211
|
def clone_to_directory(self, path=None, branch=None, head=None, depth=0):
|
|
211
212
|
"""
|
|
212
213
|
Perform a (shallow or full) clone of the Git repository in a temporary directory.
|
|
@@ -246,6 +247,8 @@ class GitRepository(PrimaryModel):
|
|
|
246
247
|
logger.info(f"Cloned repository {self.name} to {path_name}")
|
|
247
248
|
return path_name
|
|
248
249
|
|
|
250
|
+
clone_to_directory.alters_data = True
|
|
251
|
+
|
|
249
252
|
def cleanup_cloned_directory(self, path):
|
|
250
253
|
"""
|
|
251
254
|
Cleanup the cloned directory.
|
|
@@ -259,3 +262,5 @@ class GitRepository(PrimaryModel):
|
|
|
259
262
|
except OSError as os_error:
|
|
260
263
|
# log error if the cleanup fails
|
|
261
264
|
logger.error(f"Failed to cleanup temporary directory at {path}: {os_error}")
|
|
265
|
+
|
|
266
|
+
cleanup_cloned_directory.alters_data = True
|
nautobot/extras/models/groups.py
CHANGED
|
@@ -338,6 +338,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
338
338
|
|
|
339
339
|
return self.members
|
|
340
340
|
|
|
341
|
+
_set_members.alters_data = True
|
|
342
|
+
|
|
341
343
|
def add_members(self, objects_to_add):
|
|
342
344
|
"""Add the given list or QuerySet of objects to this staticly defined group."""
|
|
343
345
|
if self.group_type != DynamicGroupTypeChoices.TYPE_STATIC:
|
|
@@ -354,6 +356,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
354
356
|
objects_to_add = [obj for obj in objects_to_add if obj not in existing_members]
|
|
355
357
|
return self._add_members(objects_to_add)
|
|
356
358
|
|
|
359
|
+
add_members.alters_data = True
|
|
360
|
+
|
|
357
361
|
def _add_members(self, objects_to_add):
|
|
358
362
|
"""
|
|
359
363
|
Internal API for adding the given list or QuerySet of objects to the cached/static members of this group.
|
|
@@ -377,6 +381,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
377
381
|
]
|
|
378
382
|
StaticGroupAssociation.all_objects.bulk_create(sgas, batch_size=1000)
|
|
379
383
|
|
|
384
|
+
_add_members.alters_data = True
|
|
385
|
+
|
|
380
386
|
def remove_members(self, objects_to_remove):
|
|
381
387
|
"""Remove the given list or QuerySet of objects from this staticly defined group."""
|
|
382
388
|
if self.group_type != DynamicGroupTypeChoices.TYPE_STATIC:
|
|
@@ -390,6 +396,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
390
396
|
raise TypeError(f"{obj} is not a {self.model._meta.label_lower}")
|
|
391
397
|
return self._remove_members(objects_to_remove)
|
|
392
398
|
|
|
399
|
+
remove_members.alters_data = True
|
|
400
|
+
|
|
393
401
|
def _remove_members(self, objects_to_remove):
|
|
394
402
|
"""Internal API for removing the given list or QuerySet from the cached/static members of this Group."""
|
|
395
403
|
from nautobot.extras.signals import _handle_deleted_object # avoid circular import
|
|
@@ -418,6 +426,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
418
426
|
logger.debug("Re-connecting the _handle_deleted_object signal")
|
|
419
427
|
pre_delete.connect(_handle_deleted_object)
|
|
420
428
|
|
|
429
|
+
_remove_members.alters_data = True
|
|
430
|
+
|
|
421
431
|
@property
|
|
422
432
|
@method_deprecated("Members are now cached in the database via StaticGroupAssociations rather than in Redis.")
|
|
423
433
|
def members_cache_key(self):
|
|
@@ -451,6 +461,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
451
461
|
|
|
452
462
|
return members
|
|
453
463
|
|
|
464
|
+
update_cached_members.alters_data = True
|
|
465
|
+
|
|
454
466
|
def has_member(self, obj, use_cache=False):
|
|
455
467
|
"""
|
|
456
468
|
Return True if the given object is a member of this group.
|
|
@@ -560,6 +572,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
560
572
|
|
|
561
573
|
self.filter = new_filter
|
|
562
574
|
|
|
575
|
+
set_filter.alters_data = True
|
|
576
|
+
|
|
563
577
|
def get_initial(self):
|
|
564
578
|
"""
|
|
565
579
|
Return a form-friendly version of `self.filter` for initial form data.
|
|
@@ -815,6 +829,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
815
829
|
instance = self.children.through(parent_group=self, group=child, operator=operator, weight=weight)
|
|
816
830
|
return instance.validated_save()
|
|
817
831
|
|
|
832
|
+
add_child.alters_data = True
|
|
833
|
+
|
|
818
834
|
# TODO: unused in core
|
|
819
835
|
def remove_child(self, child):
|
|
820
836
|
"""
|
|
@@ -829,6 +845,8 @@ class DynamicGroup(PrimaryModel):
|
|
|
829
845
|
instance = self.children.through.objects.get(parent_group=self, group=child)
|
|
830
846
|
return instance.delete()
|
|
831
847
|
|
|
848
|
+
remove_child.alters_data = True
|
|
849
|
+
|
|
832
850
|
def get_descendants(self, group=None):
|
|
833
851
|
"""
|
|
834
852
|
Recursively return a list of the children of all child groups.
|
nautobot/extras/models/jobs.py
CHANGED
|
@@ -753,6 +753,8 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
753
753
|
duration.total_seconds()
|
|
754
754
|
)
|
|
755
755
|
|
|
756
|
+
set_status.alters_data = True
|
|
757
|
+
|
|
756
758
|
@classmethod
|
|
757
759
|
def execute_job(cls, *args, **kwargs):
|
|
758
760
|
"""
|
|
@@ -768,6 +770,8 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
768
770
|
"""
|
|
769
771
|
return cls.enqueue_job(*args, **kwargs, synchronous=True)
|
|
770
772
|
|
|
773
|
+
execute_job.__func__.alters_data = True
|
|
774
|
+
|
|
771
775
|
@classmethod
|
|
772
776
|
def enqueue_job(
|
|
773
777
|
cls,
|
|
@@ -938,6 +942,8 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
938
942
|
|
|
939
943
|
return job_result
|
|
940
944
|
|
|
945
|
+
enqueue_job.__func__.alters_data = True
|
|
946
|
+
|
|
941
947
|
def log(
|
|
942
948
|
self,
|
|
943
949
|
message,
|
|
@@ -989,6 +995,8 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
989
995
|
else:
|
|
990
996
|
log.save(using=JOB_LOGS)
|
|
991
997
|
|
|
998
|
+
log.alters_data = True
|
|
999
|
+
|
|
992
1000
|
|
|
993
1001
|
#
|
|
994
1002
|
# Job Button
|
|
@@ -1076,6 +1084,8 @@ class ScheduledJobs(models.Model):
|
|
|
1076
1084
|
if not instance.no_changes:
|
|
1077
1085
|
cls.update_changed()
|
|
1078
1086
|
|
|
1087
|
+
changed.__func__.alters_data = True
|
|
1088
|
+
|
|
1079
1089
|
@classmethod
|
|
1080
1090
|
def update_changed(cls, raw=False, **kwargs):
|
|
1081
1091
|
"""This function acts as a signal handler to track changes to the scheduled job that is triggered after a change"""
|
|
@@ -1083,6 +1093,8 @@ class ScheduledJobs(models.Model):
|
|
|
1083
1093
|
return
|
|
1084
1094
|
cls.objects.update_or_create(ident=1, defaults={"last_update": timezone.now()})
|
|
1085
1095
|
|
|
1096
|
+
update_changed.__func__.alters_data = True
|
|
1097
|
+
|
|
1086
1098
|
@classmethod
|
|
1087
1099
|
def last_change(cls):
|
|
1088
1100
|
"""This function acts as a getter for the last update on scheduled jobs"""
|
|
@@ -1371,6 +1383,11 @@ class ScheduledJob(BaseModel):
|
|
|
1371
1383
|
if job_model.time_limit > 0:
|
|
1372
1384
|
celery_kwargs["time_limit"] = job_model.time_limit
|
|
1373
1385
|
|
|
1386
|
+
# We do this because when a job creates an approval workflow, a scheduled job is also created.
|
|
1387
|
+
# If the scheduled job has an "immediate" interval, the scheduler will not send this task.
|
|
1388
|
+
# since TYPE_IMMEDIATELY is not a valid value in JobExecutionType.SCHEDULE_CHOICES
|
|
1389
|
+
if interval == JobExecutionType.TYPE_IMMEDIATELY:
|
|
1390
|
+
interval = JobExecutionType.TYPE_FUTURE
|
|
1374
1391
|
# 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
|
|
1375
1392
|
#
|
|
1376
1393
|
# We pass in task and job_model here partly for forward/backward compatibility logic, and
|
|
@@ -1397,6 +1414,8 @@ class ScheduledJob(BaseModel):
|
|
|
1397
1414
|
scheduled_job.validated_save()
|
|
1398
1415
|
return scheduled_job
|
|
1399
1416
|
|
|
1417
|
+
create_schedule.__func__.alters_data = True
|
|
1418
|
+
|
|
1400
1419
|
def to_cron(self):
|
|
1401
1420
|
tz = self.time_zone
|
|
1402
1421
|
t = self.start_time.astimezone(tz) # pylint: disable=no-member
|
|
@@ -251,6 +251,8 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
251
251
|
self.clean()
|
|
252
252
|
return super().validated_save(*args, **kwargs)
|
|
253
253
|
|
|
254
|
+
validated_save.alters_data = True
|
|
255
|
+
|
|
254
256
|
def clean(self):
|
|
255
257
|
"""
|
|
256
258
|
Validate a value according to the field's type validation rules.
|
nautobot/extras/models/models.py
CHANGED
|
@@ -457,6 +457,8 @@ class ExportTemplate(
|
|
|
457
457
|
if self.file_extension.startswith("."):
|
|
458
458
|
self.file_extension = self.file_extension[1:]
|
|
459
459
|
|
|
460
|
+
clean.alters_data = True
|
|
461
|
+
|
|
460
462
|
|
|
461
463
|
#
|
|
462
464
|
# External integrations
|
|
@@ -919,6 +921,8 @@ class UserSavedViewAssociation(BaseModel):
|
|
|
919
921
|
super().clean()
|
|
920
922
|
self.view_name = self.saved_view.view
|
|
921
923
|
|
|
924
|
+
clean.alters_data = True
|
|
925
|
+
|
|
922
926
|
|
|
923
927
|
#
|
|
924
928
|
# Webhooks
|
|
@@ -4,6 +4,7 @@ from django.core.exceptions import ValidationError
|
|
|
4
4
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
5
5
|
from django.db import models
|
|
6
6
|
from jinja2.exceptions import TemplateSyntaxError, UndefinedError
|
|
7
|
+
from jinja2.sandbox import unsafe
|
|
7
8
|
|
|
8
9
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
9
10
|
from nautobot.core.models import BaseModel
|
|
@@ -58,6 +59,7 @@ class Secret(PrimaryModel):
|
|
|
58
59
|
except (TemplateSyntaxError, UndefinedError) as exc:
|
|
59
60
|
raise SecretParametersError(self, registry["secrets_providers"].get(self.provider), str(exc)) from exc
|
|
60
61
|
|
|
62
|
+
@unsafe
|
|
61
63
|
def get_value(self, obj=None):
|
|
62
64
|
"""Retrieve the secret value that this Secret is a representation of.
|
|
63
65
|
|
|
@@ -77,6 +79,8 @@ class Secret(PrimaryModel):
|
|
|
77
79
|
except Exception as exc:
|
|
78
80
|
raise SecretError(self, provider, str(exc)) from exc
|
|
79
81
|
|
|
82
|
+
get_value.do_not_call_in_templates = True
|
|
83
|
+
|
|
80
84
|
def clean(self):
|
|
81
85
|
provider = registry["secrets_providers"].get(self.provider)
|
|
82
86
|
if not provider:
|
|
@@ -108,6 +112,7 @@ class SecretsGroup(OrganizationalModel):
|
|
|
108
112
|
def __str__(self):
|
|
109
113
|
return self.name
|
|
110
114
|
|
|
115
|
+
@unsafe
|
|
111
116
|
def get_secret_value(self, access_type, secret_type, obj=None, **kwargs):
|
|
112
117
|
"""Helper method to retrieve a specific secret from this group.
|
|
113
118
|
|
|
@@ -118,6 +123,8 @@ class SecretsGroup(OrganizationalModel):
|
|
|
118
123
|
).secret
|
|
119
124
|
return secret.get_value(obj=obj, **kwargs)
|
|
120
125
|
|
|
126
|
+
get_secret_value.do_not_call_in_templates = True
|
|
127
|
+
|
|
121
128
|
|
|
122
129
|
@extras_features(
|
|
123
130
|
"graphql",
|
|
@@ -81,6 +81,9 @@ class NautobotAppConfig(NautobotConfig):
|
|
|
81
81
|
config_view_name = None
|
|
82
82
|
docs_view_name = None
|
|
83
83
|
|
|
84
|
+
# Dynamic jobs. Set to True if the app's job code should be reloaded at runtime
|
|
85
|
+
provides_dynamic_jobs = False
|
|
86
|
+
|
|
84
87
|
# Default integration paths. Plugin authors can override these to customize the paths to
|
|
85
88
|
# integrated components.
|
|
86
89
|
banner_function = "banner.banner"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
|
|
3
|
+
from jinja2.sandbox import unsafe
|
|
4
|
+
|
|
3
5
|
from nautobot.extras.registry import registry
|
|
4
6
|
|
|
5
7
|
from .exceptions import SecretError, SecretParametersError, SecretProviderError, SecretValueNotFoundError
|
|
@@ -31,6 +33,7 @@ class SecretsProvider(ABC):
|
|
|
31
33
|
|
|
32
34
|
@classmethod
|
|
33
35
|
@abstractmethod
|
|
36
|
+
@unsafe
|
|
34
37
|
def get_value_for_secret(cls, secret, obj=None, **kwargs):
|
|
35
38
|
"""Retrieve the stored value described by the given Secret record.
|
|
36
39
|
|
|
@@ -41,6 +44,17 @@ class SecretsProvider(ABC):
|
|
|
41
44
|
obj (object): Django model instance or similar providing additional context for retrieving the secret.
|
|
42
45
|
"""
|
|
43
46
|
|
|
47
|
+
get_value_for_secret.__func__.do_not_call_in_templates = True
|
|
48
|
+
|
|
49
|
+
def __init_subclass__(cls, **kwargs):
|
|
50
|
+
# Automatically apply protection against Django and Jinja2 template execution to child classes.
|
|
51
|
+
if not getattr(cls.get_value_for_secret, "do_not_call_in_templates", False): # Django
|
|
52
|
+
cls.get_value_for_secret.__func__.do_not_call_in_templates = True
|
|
53
|
+
if not getattr(cls.get_value_for_secret, "unsafe_callable", False): # Jinja @unsafe decorator
|
|
54
|
+
cls.get_value_for_secret.__func__.unsafe_callable = True
|
|
55
|
+
|
|
56
|
+
super().__init_subclass__(**kwargs)
|
|
57
|
+
|
|
44
58
|
|
|
45
59
|
def register_secrets_provider(provider):
|
|
46
60
|
"""
|
nautobot/extras/tables.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
|
+
from django.db.models import QuerySet
|
|
2
3
|
from django.utils.html import format_html, format_html_join
|
|
3
4
|
import django_tables2 as tables
|
|
5
|
+
from django_tables2.data import TableData
|
|
6
|
+
from django_tables2.rows import BoundRows
|
|
4
7
|
from django_tables2.utils import Accessor
|
|
5
8
|
from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
|
|
6
9
|
|
|
10
|
+
from nautobot.core.models.querysets import count_related
|
|
7
11
|
from nautobot.core.tables import (
|
|
8
12
|
BaseTable,
|
|
9
13
|
BooleanColumn,
|
|
@@ -19,7 +23,7 @@ from nautobot.core.tables import (
|
|
|
19
23
|
from nautobot.core.templatetags.helpers import render_boolean, render_json, render_markdown
|
|
20
24
|
from nautobot.tenancy.tables import TenantColumn
|
|
21
25
|
|
|
22
|
-
from .choices import MetadataTypeDataTypeChoices
|
|
26
|
+
from .choices import LogLevelChoices, MetadataTypeDataTypeChoices
|
|
23
27
|
from .models import (
|
|
24
28
|
ComputedField,
|
|
25
29
|
ConfigContext,
|
|
@@ -891,7 +895,7 @@ class JobResultTable(BaseTable):
|
|
|
891
895
|
)
|
|
892
896
|
summary = tables.Column(
|
|
893
897
|
empty_values=(),
|
|
894
|
-
verbose_name="
|
|
898
|
+
verbose_name="Summary",
|
|
895
899
|
orderable=False,
|
|
896
900
|
attrs={"td": {"class": "text-nowrap report-stats"}},
|
|
897
901
|
)
|
|
@@ -901,6 +905,40 @@ class JobResultTable(BaseTable):
|
|
|
901
905
|
)
|
|
902
906
|
actions = ButtonsColumn(JobResult, buttons=("delete",), prepend_template=JOB_RESULT_BUTTONS)
|
|
903
907
|
|
|
908
|
+
def __init__(self, *args, **kwargs):
|
|
909
|
+
super().__init__(*args, **kwargs)
|
|
910
|
+
# Only calculate log counts for "summary" column if it's actually visible.
|
|
911
|
+
if self.columns["summary"].visible and isinstance(self.data.data, QuerySet):
|
|
912
|
+
self.data = TableData.from_data(
|
|
913
|
+
self.data.data.annotate(
|
|
914
|
+
debug_log_count=count_related(
|
|
915
|
+
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
|
|
916
|
+
),
|
|
917
|
+
success_log_count=count_related(
|
|
918
|
+
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
|
|
919
|
+
),
|
|
920
|
+
info_log_count=count_related(
|
|
921
|
+
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
|
|
922
|
+
),
|
|
923
|
+
warning_log_count=count_related(
|
|
924
|
+
JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_WARNING}
|
|
925
|
+
),
|
|
926
|
+
error_log_count=count_related(
|
|
927
|
+
JobLogEntry,
|
|
928
|
+
"job_result",
|
|
929
|
+
filter_dict={
|
|
930
|
+
"log_level__in": [
|
|
931
|
+
LogLevelChoices.LOG_FAILURE,
|
|
932
|
+
LogLevelChoices.LOG_ERROR,
|
|
933
|
+
LogLevelChoices.LOG_CRITICAL,
|
|
934
|
+
],
|
|
935
|
+
},
|
|
936
|
+
),
|
|
937
|
+
)
|
|
938
|
+
)
|
|
939
|
+
self.data.set_table(self)
|
|
940
|
+
self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data)
|
|
941
|
+
|
|
904
942
|
def render_summary(self, record):
|
|
905
943
|
"""
|
|
906
944
|
Define custom rendering for the summary column.
|
|
@@ -940,7 +978,6 @@ class JobResultTable(BaseTable):
|
|
|
940
978
|
"job_model",
|
|
941
979
|
"user",
|
|
942
980
|
"status",
|
|
943
|
-
"summary",
|
|
944
981
|
"actions",
|
|
945
982
|
)
|
|
946
983
|
|