nautobot 2.2.4__py3-none-any.whl → 2.2.6__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 +2 -0
- nautobot/apps/models.py +2 -0
- nautobot/circuits/forms.py +15 -0
- nautobot/circuits/navigation.py +9 -1
- nautobot/circuits/views.py +2 -0
- nautobot/core/api/fields.py +13 -0
- nautobot/core/api/serializers.py +7 -1
- nautobot/core/filters.py +11 -0
- nautobot/core/management/commands/generate_test_data.py +128 -158
- nautobot/core/models/fields.py +15 -0
- nautobot/core/settings.yaml +3 -3
- nautobot/core/testing/filters.py +24 -1
- nautobot/core/testing/views.py +13 -1
- nautobot/core/tests/test_utils.py +48 -1
- nautobot/core/utils/git.py +121 -49
- nautobot/core/utils/module_loading.py +10 -2
- nautobot/core/views/utils.py +18 -1
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filters/__init__.py +1 -1
- nautobot/dcim/forms.py +23 -4
- nautobot/dcim/tables/devicetypes.py +15 -4
- nautobot/dcim/tests/test_models.py +2 -0
- nautobot/dcim/tests/test_views.py +84 -0
- nautobot/dcim/views.py +3 -0
- nautobot/extras/api/views.py +2 -2
- nautobot/extras/context_managers.py +3 -0
- nautobot/extras/datasources/git.py +133 -135
- nautobot/extras/datasources/utils.py +3 -0
- nautobot/extras/filters/__init__.py +16 -1
- nautobot/extras/forms/forms.py +49 -3
- nautobot/extras/forms/mixins.py +0 -6
- nautobot/extras/jobs.py +9 -1
- nautobot/extras/migrations/0107_laxurlfield.py +28 -0
- nautobot/extras/migrations/0108_jobbutton_enabled.py +17 -0
- nautobot/extras/models/datasources.py +6 -4
- nautobot/extras/models/jobs.py +30 -0
- nautobot/extras/models/models.py +2 -4
- nautobot/extras/signals.py +6 -1
- nautobot/extras/tables.py +3 -0
- nautobot/extras/templates/extras/jobbutton_retrieve.html +6 -2
- nautobot/extras/templatetags/job_buttons.py +2 -2
- nautobot/extras/tests/git_data/01-valid-files/__init__.py +0 -0
- nautobot/extras/tests/git_data/01-valid-files/config_context_schemas/schema-1.yaml +18 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/context.yaml +12 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/devices/test-device.json +3 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/locations/Test Location.json +7 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template.j2 +3 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template2.html +4 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/ipam/vlan/template.j2 +3 -0
- nautobot/extras/tests/git_data/01-valid-files/jobs/__init__.py +5 -0
- nautobot/extras/tests/git_data/01-valid-files/jobs/my_job.py +16 -0
- nautobot/extras/tests/git_data/02-invalid-files/__init__.py +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema1.json +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema2.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext1.json +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext2.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext3.json +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/devices/nosuchdevice.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/dcim/template.j2 +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/devices/template.j2 +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/export_templates/dcim/nosuchmodel/template.j2 +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/export_templates/nosuchapp/device/template.j2 +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/__init__.py +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/importerror.py +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/syntaxerror.py +1 -0
- nautobot/extras/tests/git_helper.py +76 -0
- nautobot/extras/tests/test_api.py +52 -13
- nautobot/extras/tests/test_context_managers.py +33 -1
- nautobot/extras/tests/test_datasources.py +94 -276
- nautobot/extras/tests/test_filters.py +69 -0
- nautobot/extras/tests/test_forms.py +0 -3
- nautobot/extras/tests/test_models.py +8 -3
- nautobot/extras/tests/test_views.py +69 -11
- nautobot/extras/views.py +12 -10
- nautobot/ipam/filters.py +9 -1
- nautobot/ipam/forms.py +26 -0
- nautobot/ipam/tables.py +1 -1
- nautobot/ipam/tests/test_filters.py +15 -0
- nautobot/ipam/tests/test_views.py +9 -2
- nautobot/ipam/views.py +11 -0
- nautobot/project-static/docs/404.html +84 -9
- nautobot/project-static/docs/apps/index.html +97 -11
- nautobot/project-static/docs/apps/nautobot-apps.html +97 -11
- nautobot/project-static/docs/assets/app-icons/icon-CapacityMetrics.svg +1 -0
- nautobot/project-static/docs/assets/app-icons/icon-CircuitMaintenance.png +0 -0
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js → bundle.ad660dcc.min.js} +6 -6
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js.map → bundle.ad660dcc.min.js.map} +3 -3
- nautobot/project-static/docs/assets/javascripts/glightbox.min.js +1 -0
- nautobot/project-static/docs/assets/stylesheets/glightbox.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css → main.6543a935.min.css} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css.map → main.6543a935.min.css.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +157 -13
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +144 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +97 -11
- nautobot/project-static/docs/development/apps/api/configuration-view.html +97 -11
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/graphql.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +98 -12
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +97 -11
- nautobot/project-static/docs/development/apps/api/prometheus.html +97 -11
- nautobot/project-static/docs/development/apps/api/setup.html +97 -11
- nautobot/project-static/docs/development/apps/api/testing.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/base-template.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/notes.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/urls.html +97 -11
- nautobot/project-static/docs/development/apps/index.html +97 -11
- nautobot/project-static/docs/development/apps/migration/code-updates.html +98 -12
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +97 -11
- nautobot/project-static/docs/development/apps/migration/from-v1.html +99 -13
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +97 -11
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +97 -11
- nautobot/project-static/docs/development/core/application-registry.html +98 -12
- nautobot/project-static/docs/development/core/best-practices.html +97 -11
- nautobot/project-static/docs/development/core/bootstrap-ui.html +97 -11
- nautobot/project-static/docs/development/core/caching.html +97 -11
- nautobot/project-static/docs/development/core/controllers.html +97 -11
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +97 -11
- nautobot/project-static/docs/development/core/generic-views.html +97 -11
- nautobot/project-static/docs/development/core/getting-started.html +99 -13
- nautobot/project-static/docs/development/core/homepage.html +97 -11
- nautobot/project-static/docs/development/core/index.html +97 -11
- nautobot/project-static/docs/development/core/model-checklist.html +97 -11
- nautobot/project-static/docs/development/core/model-features.html +97 -11
- nautobot/project-static/docs/development/core/natural-keys.html +97 -11
- nautobot/project-static/docs/development/core/navigation-menu.html +97 -11
- nautobot/project-static/docs/development/core/release-checklist.html +97 -11
- nautobot/project-static/docs/development/core/role-internals.html +97 -11
- nautobot/project-static/docs/development/core/settings.html +97 -11
- nautobot/project-static/docs/development/core/style-guide.html +97 -11
- nautobot/project-static/docs/development/core/templates.html +97 -11
- nautobot/project-static/docs/development/core/testing.html +98 -12
- nautobot/project-static/docs/development/core/user-preferences.html +97 -11
- nautobot/project-static/docs/development/index.html +97 -11
- nautobot/project-static/docs/development/jobs/index.html +97 -11
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +97 -11
- nautobot/project-static/docs/index.html +13 -8362
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +8229 -0
- nautobot/project-static/docs/overview/design_philosophy.html +8158 -0
- nautobot/project-static/docs/overview/index.html +8230 -0
- nautobot/project-static/docs/release-notes/index.html +97 -11
- nautobot/project-static/docs/release-notes/version-1.0.html +98 -12
- nautobot/project-static/docs/release-notes/version-1.1.html +97 -11
- nautobot/project-static/docs/release-notes/version-1.2.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.3.html +98 -12
- nautobot/project-static/docs/release-notes/version-1.4.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.5.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.6.html +626 -160
- nautobot/project-static/docs/release-notes/version-2.0.html +100 -14
- nautobot/project-static/docs/release-notes/version-2.1.html +97 -11
- nautobot/project-static/docs/release-notes/version-2.2.html +546 -107
- nautobot/project-static/docs/requirements.txt +3 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +268 -258
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +99 -13
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +98 -12
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +100 -14
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +98 -12
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/index.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +101 -15
- nautobot/project-static/docs/user-guide/administration/installation/services.html +98 -12
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +97 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +97 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +97 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +97 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +98 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +97 -30
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +100 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +99 -13
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +98 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +100 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +98 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +98 -12
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +97 -11
- nautobot/project-static/docs/user-guide/index.html +100 -14
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +98 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +98 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +102 -15
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +98 -44
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +97 -11
- nautobot/project-static/js/forms.js +2 -1
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/views.py +1 -0
- nautobot/virtualization/forms.py +18 -6
- nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
- nautobot/virtualization/views.py +2 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/METADATA +1 -1
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/RECORD +364 -331
- nautobot/extras/tests/test_git.py +0 -23
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/NOTICE +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/WHEEL +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/entry_points.txt +0 -0
nautobot/extras/api/views.py
CHANGED
|
@@ -304,13 +304,13 @@ class DynamicGroupViewSet(NotesViewSetMixin, ModelViewSet):
|
|
|
304
304
|
# @extend_schema(methods=["get"], responses={200: member_response})
|
|
305
305
|
@action(detail=True, methods=["get"])
|
|
306
306
|
def members(self, request, pk, *args, **kwargs):
|
|
307
|
-
"""List member objects of
|
|
307
|
+
"""List the member objects of this dynamic group."""
|
|
308
308
|
instance = get_object_or_404(self.queryset, pk=pk)
|
|
309
309
|
|
|
310
310
|
# Retrieve the serializer for the content_type and paginate the results
|
|
311
311
|
member_model_class = instance.content_type.model_class()
|
|
312
312
|
member_serializer_class = get_serializer_for_model(member_model_class)
|
|
313
|
-
members = self.paginate_queryset(instance.members)
|
|
313
|
+
members = self.paginate_queryset(instance.members.restrict(request.user, "view"))
|
|
314
314
|
member_serializer = member_serializer_class(members, many=True, context={"request": request})
|
|
315
315
|
return self.get_paginated_response(member_serializer.data)
|
|
316
316
|
|
|
@@ -91,9 +91,12 @@ class ChangeContext:
|
|
|
91
91
|
for entry in self.deferred_object_changes[key]:
|
|
92
92
|
objectchange = entry["instance"].to_objectchange(entry["action"])
|
|
93
93
|
objectchange.user = entry["user"]
|
|
94
|
+
objectchange.user_name = objectchange.user.username
|
|
94
95
|
objectchange.request_id = self.change_id
|
|
95
96
|
objectchange.change_context = self.context
|
|
96
97
|
objectchange.change_context_detail = self.context_detail[:CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL]
|
|
98
|
+
if not objectchange.changed_object_id:
|
|
99
|
+
objectchange.changed_object_id = entry.get("changed_object_id")
|
|
97
100
|
create_object_changes.append(objectchange)
|
|
98
101
|
self.deferred_object_changes.pop(key, None)
|
|
99
102
|
ObjectChange.objects.bulk_create(create_object_changes, batch_size=batch_size)
|
|
@@ -136,7 +136,7 @@ def get_repo_from_url_to_path_and_from_branch(repository_record):
|
|
|
136
136
|
|
|
137
137
|
def ensure_git_repository(repository_record, logger=None, head=None): # pylint: disable=redefined-outer-name
|
|
138
138
|
"""Ensure that the given Git repo is present, up-to-date, and has the correct branch selected.
|
|
139
|
-
Note that this function may be called independently of the `
|
|
139
|
+
Note that this function may be called independently of the `GitRepositorySync` job,
|
|
140
140
|
such as to ensure that different Nautobot instances and/or worker instances all have a local copy of the same HEAD.
|
|
141
141
|
Args:
|
|
142
142
|
repository_record (GitRepository): Repository to ensure the state of.
|
|
@@ -224,126 +224,127 @@ def refresh_git_config_contexts(repository_record, job_result, delete=False):
|
|
|
224
224
|
def update_git_config_contexts(repository_record, job_result):
|
|
225
225
|
"""Refresh any config contexts provided by this Git repository."""
|
|
226
226
|
config_context_path = os.path.join(repository_record.filesystem_path, "config_contexts")
|
|
227
|
-
if not os.path.isdir(config_context_path):
|
|
228
|
-
return
|
|
229
|
-
|
|
230
227
|
managed_config_contexts = set()
|
|
231
228
|
managed_local_config_contexts = defaultdict(set)
|
|
232
229
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
job_result.log(msg, grouping="config contexts")
|
|
241
|
-
try:
|
|
242
|
-
with open(os.path.join(config_context_path, file_name), "r") as fd:
|
|
243
|
-
# The data file can be either JSON or YAML; since YAML is a superset of JSON, we can load it regardless
|
|
244
|
-
context_data = yaml.safe_load(fd)
|
|
245
|
-
|
|
246
|
-
# A file can contain one config context dict or a list thereof
|
|
247
|
-
if isinstance(context_data, dict):
|
|
248
|
-
context_name = import_config_context(context_data, repository_record, job_result)
|
|
249
|
-
managed_config_contexts.add(context_name)
|
|
250
|
-
elif isinstance(context_data, list):
|
|
251
|
-
for context_data_entry in context_data:
|
|
252
|
-
context_name = import_config_context(context_data_entry, repository_record, job_result)
|
|
253
|
-
managed_config_contexts.add(context_name)
|
|
254
|
-
else:
|
|
255
|
-
raise RuntimeError("data must be a dict or list of dicts")
|
|
256
|
-
|
|
257
|
-
except Exception as exc:
|
|
258
|
-
msg = f"Error in loading config context data from `{file_name}`: {exc}"
|
|
259
|
-
logger.error(msg)
|
|
260
|
-
job_result.log(msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="config contexts")
|
|
261
|
-
|
|
262
|
-
# Next, handle the "filter/name" directory structure case - files in <filter_type>/<name>.(json|yaml)
|
|
263
|
-
for filter_type in (
|
|
264
|
-
"locations",
|
|
265
|
-
"device_types",
|
|
266
|
-
"roles",
|
|
267
|
-
"platforms",
|
|
268
|
-
"cluster_groups",
|
|
269
|
-
"clusters",
|
|
270
|
-
"tenant_groups",
|
|
271
|
-
"tenants",
|
|
272
|
-
"tags",
|
|
273
|
-
"dynamic_groups",
|
|
274
|
-
"device_redundancy_groups",
|
|
275
|
-
):
|
|
276
|
-
if os.path.isdir(os.path.join(repository_record.filesystem_path, filter_type)):
|
|
277
|
-
msg = (
|
|
278
|
-
f'Found "{filter_type}" directory in the repository root. If this is meant to contain config contexts, '
|
|
279
|
-
"it should be moved into a `config_contexts/` subdirectory."
|
|
280
|
-
)
|
|
281
|
-
logger.warning(msg)
|
|
282
|
-
job_result.log(msg, level_choice=LogLevelChoices.LOG_WARNING, grouping="config contexts")
|
|
283
|
-
|
|
284
|
-
dir_path = os.path.join(config_context_path, filter_type)
|
|
285
|
-
if not os.path.isdir(dir_path):
|
|
286
|
-
continue
|
|
287
|
-
|
|
288
|
-
for file_name in os.listdir(dir_path):
|
|
289
|
-
name = os.path.splitext(file_name)[0]
|
|
290
|
-
msg = f'Loading config context, filter `{filter_type} = [name: "{name}"]`, from `{filter_type}/{file_name}`'
|
|
230
|
+
if os.path.isdir(config_context_path):
|
|
231
|
+
# First, handle the "flat file" case - data files in the root config_context_path,
|
|
232
|
+
# whose metadata is expressed purely within the contents of the file:
|
|
233
|
+
for file_name in os.listdir(config_context_path):
|
|
234
|
+
if not os.path.isfile(os.path.join(config_context_path, file_name)):
|
|
235
|
+
continue
|
|
236
|
+
msg = f"Loading config context from `{file_name}`"
|
|
291
237
|
logger.info(msg)
|
|
292
238
|
job_result.log(msg, grouping="config contexts")
|
|
293
239
|
try:
|
|
294
|
-
with open(os.path.join(
|
|
295
|
-
#
|
|
240
|
+
with open(os.path.join(config_context_path, file_name), "r") as fd:
|
|
241
|
+
# The data file can be either JSON or YAML; since YAML is a superset of JSON, we load it regardless
|
|
296
242
|
context_data = yaml.safe_load(fd)
|
|
297
243
|
|
|
298
|
-
#
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
244
|
+
# A file can contain one config context dict or a list thereof
|
|
245
|
+
if isinstance(context_data, dict):
|
|
246
|
+
context_name = import_config_context(context_data, repository_record, job_result)
|
|
247
|
+
managed_config_contexts.add(context_name)
|
|
248
|
+
elif isinstance(context_data, list):
|
|
249
|
+
for context_data_entry in context_data:
|
|
250
|
+
context_name = import_config_context(context_data_entry, repository_record, job_result)
|
|
251
|
+
managed_config_contexts.add(context_name)
|
|
303
252
|
else:
|
|
304
|
-
|
|
253
|
+
raise RuntimeError("data must be a dict or list of dicts")
|
|
305
254
|
|
|
306
|
-
context_name = import_config_context(context_data, repository_record, job_result)
|
|
307
|
-
managed_config_contexts.add(context_name)
|
|
308
255
|
except Exception as exc:
|
|
309
256
|
msg = f"Error in loading config context data from `{file_name}`: {exc}"
|
|
310
257
|
logger.error(msg)
|
|
311
258
|
job_result.log(msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="config contexts")
|
|
312
259
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
260
|
+
# Next, handle the "filter/name" directory structure case - files in <filter_type>/<name>.(json|yaml)
|
|
261
|
+
for filter_type in (
|
|
262
|
+
"locations",
|
|
263
|
+
"device_types",
|
|
264
|
+
"roles",
|
|
265
|
+
"platforms",
|
|
266
|
+
"cluster_groups",
|
|
267
|
+
"clusters",
|
|
268
|
+
"tenant_groups",
|
|
269
|
+
"tenants",
|
|
270
|
+
"tags",
|
|
271
|
+
"dynamic_groups",
|
|
272
|
+
"device_redundancy_groups",
|
|
273
|
+
):
|
|
274
|
+
if os.path.isdir(os.path.join(repository_record.filesystem_path, filter_type)):
|
|
275
|
+
msg = (
|
|
276
|
+
f'Found "{filter_type}" directory in the repository root. If this is meant to contain config contexts, '
|
|
277
|
+
"it should be moved into a `config_contexts/` subdirectory."
|
|
278
|
+
)
|
|
279
|
+
logger.warning(msg)
|
|
280
|
+
job_result.log(msg, level_choice=LogLevelChoices.LOG_WARNING, grouping="config contexts")
|
|
322
281
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
282
|
+
dir_path = os.path.join(config_context_path, filter_type)
|
|
283
|
+
if not os.path.isdir(dir_path):
|
|
284
|
+
continue
|
|
326
285
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
286
|
+
for file_name in os.listdir(dir_path):
|
|
287
|
+
name = os.path.splitext(file_name)[0]
|
|
288
|
+
msg = (
|
|
289
|
+
f'Loading config context, filter `{filter_type} = [name: "{name}"]`, '
|
|
290
|
+
f"from `{filter_type}/{file_name}`"
|
|
291
|
+
)
|
|
292
|
+
logger.info(msg)
|
|
293
|
+
job_result.log(msg, grouping="config contexts")
|
|
294
|
+
try:
|
|
295
|
+
with open(os.path.join(dir_path, file_name), "r") as fd:
|
|
296
|
+
# Data file can be either JSON or YAML; since YAML is a superset of JSON, we load it regardless
|
|
297
|
+
context_data = yaml.safe_load(fd)
|
|
298
|
+
|
|
299
|
+
# Unlike the above case, these files always contain just a single config context record
|
|
300
|
+
|
|
301
|
+
# Add the implied filter to the context metadata
|
|
302
|
+
if filter_type == "device_types":
|
|
303
|
+
context_data.setdefault("_metadata", {}).setdefault(filter_type, []).append({"model": name})
|
|
304
|
+
else:
|
|
305
|
+
context_data.setdefault("_metadata", {}).setdefault(filter_type, []).append({"name": name})
|
|
335
306
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
307
|
+
context_name = import_config_context(context_data, repository_record, job_result)
|
|
308
|
+
managed_config_contexts.add(context_name)
|
|
309
|
+
except Exception as exc:
|
|
310
|
+
msg = f"Error in loading config context data from `{file_name}`: {exc}"
|
|
311
|
+
logger.error(msg)
|
|
312
|
+
job_result.log(msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="config contexts")
|
|
313
|
+
|
|
314
|
+
# Finally, handle device- and VM-specific "local" context in (devices|virtual_machines)/<name>.(json|yaml)
|
|
315
|
+
for local_type in ("devices", "virtual_machines"):
|
|
316
|
+
if os.path.isdir(os.path.join(repository_record.filesystem_path, local_type)):
|
|
317
|
+
msg = (
|
|
318
|
+
f'Found "{local_type}" directory in the repository root. If this is meant to contain '
|
|
319
|
+
"config contexts, it should be moved into a `config_contexts/` subdirectory."
|
|
341
320
|
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
321
|
+
logger.warning(msg)
|
|
322
|
+
job_result.log(msg, level_choice=LogLevelChoices.LOG_WARNING, grouping="config contexts")
|
|
323
|
+
|
|
324
|
+
dir_path = os.path.join(config_context_path, local_type)
|
|
325
|
+
if not os.path.isdir(dir_path):
|
|
326
|
+
continue
|
|
327
|
+
|
|
328
|
+
for file_name in os.listdir(dir_path):
|
|
329
|
+
device_name = os.path.splitext(file_name)[0]
|
|
330
|
+
msg = f"Loading local config context for `{device_name}` from `{local_type}/{file_name}`"
|
|
331
|
+
logger.info(msg)
|
|
332
|
+
job_result.log(msg, grouping="local config contexts")
|
|
333
|
+
try:
|
|
334
|
+
with open(os.path.join(dir_path, file_name), "r") as fd:
|
|
335
|
+
context_data = yaml.safe_load(fd)
|
|
336
|
+
|
|
337
|
+
import_local_config_context(
|
|
338
|
+
local_type,
|
|
339
|
+
device_name,
|
|
340
|
+
context_data,
|
|
341
|
+
repository_record,
|
|
342
|
+
)
|
|
343
|
+
managed_local_config_contexts[local_type].add(device_name)
|
|
344
|
+
except Exception as exc:
|
|
345
|
+
msg = f"Error in loading local config context from `{local_type}/{file_name}`: {exc}"
|
|
346
|
+
logger.error(msg)
|
|
347
|
+
job_result.log(msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="local config contexts")
|
|
347
348
|
|
|
348
349
|
# Delete any prior contexts that are owned by this repository but were not created/updated above
|
|
349
350
|
delete_git_config_contexts(
|
|
@@ -599,39 +600,38 @@ def refresh_git_config_context_schemas(repository_record, job_result, delete=Fal
|
|
|
599
600
|
def update_git_config_context_schemas(repository_record, job_result):
|
|
600
601
|
"""Refresh any config context schemas provided by this Git repository."""
|
|
601
602
|
config_context_schema_path = os.path.join(repository_record.filesystem_path, "config_context_schemas")
|
|
602
|
-
if not os.path.isdir(config_context_schema_path):
|
|
603
|
-
return
|
|
604
603
|
|
|
605
604
|
managed_config_context_schemas = set()
|
|
606
605
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
606
|
+
if os.path.isdir(config_context_schema_path):
|
|
607
|
+
for file_name in os.listdir(config_context_schema_path):
|
|
608
|
+
if not os.path.isfile(os.path.join(config_context_schema_path, file_name)):
|
|
609
|
+
continue
|
|
610
|
+
msg = (f"Loading config context schema from `{file_name}`",)
|
|
611
|
+
logger.info(msg)
|
|
612
|
+
job_result.log(msg, grouping="config context schemas")
|
|
613
|
+
try:
|
|
614
|
+
with open(os.path.join(config_context_schema_path, file_name), "r") as fd:
|
|
615
|
+
# The data file can be either JSON or YAML; since YAML is a superset of JSON, we load it regardless
|
|
616
|
+
context_schema_data = yaml.safe_load(fd)
|
|
617
|
+
|
|
618
|
+
# A file can contain one config context dict or a list thereof
|
|
619
|
+
if isinstance(context_schema_data, dict):
|
|
620
|
+
context_name = import_config_context_schema(context_schema_data, repository_record, job_result)
|
|
621
|
+
managed_config_context_schemas.add(context_name)
|
|
622
|
+
elif isinstance(context_schema_data, list):
|
|
623
|
+
for context_schema in context_schema_data:
|
|
624
|
+
if isinstance(context_schema, dict):
|
|
625
|
+
context_name = import_config_context_schema(context_schema, repository_record, job_result)
|
|
626
|
+
managed_config_context_schemas.add(context_name)
|
|
627
|
+
else:
|
|
628
|
+
raise RuntimeError("each item in list data must be a dict")
|
|
629
|
+
else:
|
|
630
|
+
raise RuntimeError("data must be a dict or a list of dicts")
|
|
631
|
+
except Exception as exc:
|
|
632
|
+
msg = f"Error in loading config context schema data from `{file_name}`: {exc}"
|
|
633
|
+
logger.error(msg)
|
|
634
|
+
job_result.log(msg, level_choice=LogLevelChoices.LOG_ERROR, grouping="config context schemas")
|
|
635
635
|
|
|
636
636
|
# Delete any prior contexts that are owned by this repository but were not created/updated above
|
|
637
637
|
delete_git_config_context_schemas(
|
|
@@ -835,12 +835,10 @@ def update_git_export_templates(repository_record, job_result):
|
|
|
835
835
|
job_result.log(msg, level_choice=LogLevelChoices.LOG_WARNING, grouping="export templates")
|
|
836
836
|
|
|
837
837
|
export_template_path = os.path.join(repository_record.filesystem_path, "export_templates")
|
|
838
|
-
|
|
839
|
-
return
|
|
838
|
+
managed_export_templates = {}
|
|
840
839
|
|
|
841
840
|
git_repository_content_type = ContentType.objects.get_for_model(GitRepository)
|
|
842
841
|
|
|
843
|
-
managed_export_templates = {}
|
|
844
842
|
for model_content_type, file_path in files_from_contenttype_directories(
|
|
845
843
|
export_template_path, job_result, "export templates"
|
|
846
844
|
):
|
|
@@ -15,6 +15,9 @@ def files_from_contenttype_directories(base_path, job_result, log_grouping):
|
|
|
15
15
|
Returns:
|
|
16
16
|
(Tuple[ContentType, file_path]): A tuple of the ContentType and the file path.
|
|
17
17
|
"""
|
|
18
|
+
if not os.path.isdir(base_path):
|
|
19
|
+
return
|
|
20
|
+
|
|
18
21
|
for app_label in os.listdir(base_path):
|
|
19
22
|
app_label_path = os.path.join(base_path, app_label)
|
|
20
23
|
if not os.path.isdir(app_label_path):
|
|
@@ -504,7 +504,7 @@ class ContactFilterSet(ContactTeamFilterSet):
|
|
|
504
504
|
fields = "__all__"
|
|
505
505
|
|
|
506
506
|
|
|
507
|
-
class ContactAssociationFilterSet(NautobotFilterSet):
|
|
507
|
+
class ContactAssociationFilterSet(NautobotFilterSet, StatusModelFilterSetMixin, RoleModelFilterSetMixin):
|
|
508
508
|
q = SearchFilter(
|
|
509
509
|
filter_predicates={
|
|
510
510
|
"contact__name": "icontains",
|
|
@@ -512,6 +512,19 @@ class ContactAssociationFilterSet(NautobotFilterSet):
|
|
|
512
512
|
},
|
|
513
513
|
)
|
|
514
514
|
|
|
515
|
+
contact = NaturalKeyOrPKMultipleChoiceFilter(
|
|
516
|
+
queryset=Contact.objects.all(),
|
|
517
|
+
to_field_name="name",
|
|
518
|
+
label="Contact (name or ID)",
|
|
519
|
+
)
|
|
520
|
+
team = NaturalKeyOrPKMultipleChoiceFilter(
|
|
521
|
+
queryset=Team.objects.all(),
|
|
522
|
+
to_field_name="name",
|
|
523
|
+
label="Team (name or ID)",
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
associated_object_type = ContentTypeFilter()
|
|
527
|
+
|
|
515
528
|
class Meta:
|
|
516
529
|
model = ContactAssociation
|
|
517
530
|
fields = "__all__"
|
|
@@ -610,6 +623,7 @@ class ExportTemplateFilterSet(BaseFilterSet):
|
|
|
610
623
|
},
|
|
611
624
|
)
|
|
612
625
|
owner_content_type = ContentTypeFilter()
|
|
626
|
+
content_type = ContentTypeFilter()
|
|
613
627
|
|
|
614
628
|
class Meta:
|
|
615
629
|
model = ExportTemplate
|
|
@@ -888,6 +902,7 @@ class JobButtonFilterSet(BaseFilterSet):
|
|
|
888
902
|
fields = (
|
|
889
903
|
"content_types",
|
|
890
904
|
"name",
|
|
905
|
+
"enabled",
|
|
891
906
|
"text",
|
|
892
907
|
"job",
|
|
893
908
|
"weight",
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -27,6 +27,7 @@ from nautobot.core.forms import (
|
|
|
27
27
|
DynamicModelMultipleChoiceField,
|
|
28
28
|
JSONArrayFormField,
|
|
29
29
|
JSONField,
|
|
30
|
+
LaxURLField,
|
|
30
31
|
MultipleContentTypeField,
|
|
31
32
|
SlugField,
|
|
32
33
|
StaticSelect2,
|
|
@@ -104,6 +105,7 @@ __all__ = (
|
|
|
104
105
|
"ConfigContextSchemaBulkEditForm",
|
|
105
106
|
"ConfigContextSchemaFilterForm",
|
|
106
107
|
"CustomFieldForm",
|
|
108
|
+
"CustomFieldFilterForm",
|
|
107
109
|
"CustomFieldModelCSVForm",
|
|
108
110
|
"CustomFieldBulkCreateForm", # 2.0 TODO remove this deprecated class
|
|
109
111
|
"CustomFieldChoiceFormSet",
|
|
@@ -114,6 +116,7 @@ __all__ = (
|
|
|
114
116
|
"DynamicGroupMembershipFormSet",
|
|
115
117
|
"ExportTemplateForm",
|
|
116
118
|
"ExportTemplateFilterForm",
|
|
119
|
+
"ExternalIntegrationFilterForm",
|
|
117
120
|
"ExternalIntegrationForm",
|
|
118
121
|
"ExternalIntegrationBulkEditForm",
|
|
119
122
|
"GitRepositoryForm",
|
|
@@ -144,6 +147,7 @@ __all__ = (
|
|
|
144
147
|
"RelationshipFilterForm",
|
|
145
148
|
"RelationshipAssociationFilterForm",
|
|
146
149
|
"RoleBulkEditForm",
|
|
150
|
+
"RoleFilterForm",
|
|
147
151
|
"RoleForm",
|
|
148
152
|
"ScheduledJobFilterForm",
|
|
149
153
|
"SecretForm",
|
|
@@ -421,6 +425,17 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
421
425
|
self.fields["key"].widget.attrs["readonly"] = True
|
|
422
426
|
|
|
423
427
|
|
|
428
|
+
class CustomFieldFilterForm(NautobotFilterForm):
|
|
429
|
+
model = CustomField
|
|
430
|
+
q = forms.CharField(required=False, label="Search")
|
|
431
|
+
content_types = MultipleContentTypeField(
|
|
432
|
+
queryset=ContentType.objects.filter(FeatureQuery("custom_fields").get_query()),
|
|
433
|
+
choices_as_strings=True,
|
|
434
|
+
required=False,
|
|
435
|
+
label="Content Type(s)",
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
424
439
|
class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelFormMixin):
|
|
425
440
|
"""
|
|
426
441
|
Base class for CSV/JSON/YAML import of models that support custom fields.
|
|
@@ -638,6 +653,14 @@ class ExternalIntegrationBulkEditForm(NautobotBulkEditForm):
|
|
|
638
653
|
nullable_fields = ["extra_config", "secrets_group", "headers"]
|
|
639
654
|
|
|
640
655
|
|
|
656
|
+
class ExternalIntegrationFilterForm(NautobotFilterForm):
|
|
657
|
+
model = ExternalIntegration
|
|
658
|
+
q = forms.CharField(required=False, label="Search")
|
|
659
|
+
secrets_group = DynamicModelMultipleChoiceField(
|
|
660
|
+
queryset=SecretsGroup.objects.all(), to_field_name="name", required=False
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
|
|
641
664
|
#
|
|
642
665
|
# Git repositories and other data sources
|
|
643
666
|
#
|
|
@@ -665,7 +688,7 @@ class PasswordInputWithPlaceholder(forms.PasswordInput):
|
|
|
665
688
|
class GitRepositoryForm(NautobotModelForm):
|
|
666
689
|
slug = SlugField(help_text="Filesystem-friendly unique shorthand")
|
|
667
690
|
|
|
668
|
-
remote_url =
|
|
691
|
+
remote_url = LaxURLField(
|
|
669
692
|
required=True,
|
|
670
693
|
label="Remote URL",
|
|
671
694
|
help_text="Only http:// and https:// URLs are presently supported",
|
|
@@ -716,7 +739,7 @@ class GitRepositoryBulkEditForm(NautobotBulkEditForm):
|
|
|
716
739
|
queryset=GitRepository.objects.all(),
|
|
717
740
|
widget=forms.MultipleHiddenInput(),
|
|
718
741
|
)
|
|
719
|
-
remote_url =
|
|
742
|
+
remote_url = LaxURLField(
|
|
720
743
|
label="Remote URL",
|
|
721
744
|
required=False,
|
|
722
745
|
)
|
|
@@ -990,6 +1013,10 @@ class JobHookForm(BootstrapMixin, forms.ModelForm):
|
|
|
990
1013
|
content_types = MultipleContentTypeField(
|
|
991
1014
|
queryset=ChangeLoggedModelsQuery().as_queryset(), required=True, label="Content Type(s)"
|
|
992
1015
|
)
|
|
1016
|
+
job = DynamicModelChoiceField(
|
|
1017
|
+
queryset=Job.objects.filter(is_job_hook_receiver=True),
|
|
1018
|
+
query_params={"is_job_hook_receiver": True},
|
|
1019
|
+
)
|
|
993
1020
|
|
|
994
1021
|
class Meta:
|
|
995
1022
|
model = JobHook
|
|
@@ -1157,14 +1184,19 @@ class JobButtonForm(BootstrapMixin, forms.ModelForm):
|
|
|
1157
1184
|
api_url="/api/extras/content-types/",
|
|
1158
1185
|
),
|
|
1159
1186
|
)
|
|
1187
|
+
job = DynamicModelChoiceField(
|
|
1188
|
+
queryset=Job.objects.filter(is_job_button_receiver=True),
|
|
1189
|
+
query_params={"is_job_button_receiver": True},
|
|
1190
|
+
)
|
|
1160
1191
|
|
|
1161
1192
|
class Meta:
|
|
1162
1193
|
model = JobButton
|
|
1163
1194
|
fields = (
|
|
1164
1195
|
"content_types",
|
|
1165
1196
|
"name",
|
|
1166
|
-
"text",
|
|
1167
1197
|
"job",
|
|
1198
|
+
"enabled",
|
|
1199
|
+
"text",
|
|
1168
1200
|
"weight",
|
|
1169
1201
|
"group_name",
|
|
1170
1202
|
"button_class",
|
|
@@ -1184,6 +1216,9 @@ class JobButtonBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
1184
1216
|
),
|
|
1185
1217
|
required=False,
|
|
1186
1218
|
)
|
|
1219
|
+
enabled = forms.NullBooleanField(
|
|
1220
|
+
required=False, widget=BulkEditNullBooleanSelect, help_text="Whether this job button appears in the UI"
|
|
1221
|
+
)
|
|
1187
1222
|
weight = forms.IntegerField(required=False)
|
|
1188
1223
|
group_name = forms.CharField(required=False)
|
|
1189
1224
|
|
|
@@ -1429,6 +1464,17 @@ class RoleBulkEditForm(NautobotBulkEditForm):
|
|
|
1429
1464
|
nullable_fields = ["weight"]
|
|
1430
1465
|
|
|
1431
1466
|
|
|
1467
|
+
class RoleFilterForm(NautobotFilterForm):
|
|
1468
|
+
model = Role
|
|
1469
|
+
q = forms.CharField(required=False, label="Search")
|
|
1470
|
+
content_types = MultipleContentTypeField(
|
|
1471
|
+
queryset=RoleModelsQuery().as_queryset(),
|
|
1472
|
+
required=False,
|
|
1473
|
+
choices_as_strings=True,
|
|
1474
|
+
label="Content Type(s)",
|
|
1475
|
+
)
|
|
1476
|
+
|
|
1477
|
+
|
|
1432
1478
|
#
|
|
1433
1479
|
# Secrets
|
|
1434
1480
|
#
|
nautobot/extras/forms/mixins.py
CHANGED
|
@@ -45,7 +45,6 @@ __all__ = (
|
|
|
45
45
|
# 2.0 TODO: remove the below deprecated aliases
|
|
46
46
|
"AddRemoveTagsForm",
|
|
47
47
|
"CustomFieldBulkEditForm",
|
|
48
|
-
"CustomFieldFilterForm",
|
|
49
48
|
"CustomFieldModelForm",
|
|
50
49
|
"RelationshipModelForm",
|
|
51
50
|
"RoleModelBulkEditFormMixin",
|
|
@@ -767,11 +766,6 @@ class CustomFieldBulkEditForm(CustomFieldModelBulkEditFormMixin):
|
|
|
767
766
|
pass
|
|
768
767
|
|
|
769
768
|
|
|
770
|
-
@class_deprecated_in_favor_of(CustomFieldModelFilterFormMixin)
|
|
771
|
-
class CustomFieldFilterForm(CustomFieldModelFilterFormMixin):
|
|
772
|
-
pass
|
|
773
|
-
|
|
774
|
-
|
|
775
769
|
@class_deprecated_in_favor_of(CustomFieldModelFormMixin)
|
|
776
770
|
class CustomFieldModelForm(CustomFieldModelFormMixin):
|
|
777
771
|
pass
|
nautobot/extras/jobs.py
CHANGED
|
@@ -1168,6 +1168,14 @@ def enqueue_job_hooks(object_change):
|
|
|
1168
1168
|
job_hooks = JobHook.objects.filter(content_types=content_type, enabled=True, **{action_flag: True})
|
|
1169
1169
|
|
|
1170
1170
|
# Enqueue the jobs related to the job_hooks
|
|
1171
|
+
get_jobs(reload=True)
|
|
1171
1172
|
for job_hook in job_hooks:
|
|
1172
1173
|
job_model = job_hook.job
|
|
1173
|
-
|
|
1174
|
+
if not job_model.installed or not job_model.enabled:
|
|
1175
|
+
logger.warning(
|
|
1176
|
+
"JobHook %s is enabled, but the underlying Job %s is not installed and enabled", job_hook, job_model
|
|
1177
|
+
)
|
|
1178
|
+
elif get_job(job_model.class_path) is None:
|
|
1179
|
+
logger.error("JobHook %s is enabled, but the underlying Job implementation is missing", job_hook)
|
|
1180
|
+
else:
|
|
1181
|
+
JobResult.enqueue_job(job_model, object_change.user, object_change=object_change.pk)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated by Django 3.2.25 on 2024-06-17 13:24
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
import nautobot.core.models.fields
|
|
6
|
+
import nautobot.core.models.validators
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Migration(migrations.Migration):
|
|
10
|
+
dependencies = [
|
|
11
|
+
("extras", "0106_populate_default_statuses_and_roles_for_contact_associations"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.AlterField(
|
|
16
|
+
model_name="externalintegration",
|
|
17
|
+
name="remote_url",
|
|
18
|
+
field=nautobot.core.models.fields.LaxURLField(max_length=500),
|
|
19
|
+
),
|
|
20
|
+
migrations.AlterField(
|
|
21
|
+
model_name="gitrepository",
|
|
22
|
+
name="remote_url",
|
|
23
|
+
field=nautobot.core.models.fields.LaxURLField(
|
|
24
|
+
max_length=255,
|
|
25
|
+
validators=[nautobot.core.models.validators.EnhancedURLValidator(schemes=["http", "https"])],
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 3.2.25 on 2024-06-17 19:06
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("extras", "0107_laxurlfield"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AddField(
|
|
13
|
+
model_name="jobbutton",
|
|
14
|
+
name="enabled",
|
|
15
|
+
field=models.BooleanField(default=True),
|
|
16
|
+
),
|
|
17
|
+
]
|