nautobot 2.2.2__py3-none-any.whl → 2.2.4__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/jobs.py +2 -0
- nautobot/core/api/utils.py +12 -9
- nautobot/core/apps/__init__.py +2 -2
- nautobot/core/celery/__init__.py +79 -68
- nautobot/core/celery/backends.py +9 -1
- nautobot/core/celery/control.py +4 -7
- nautobot/core/celery/schedulers.py +4 -2
- nautobot/core/celery/task.py +78 -5
- nautobot/core/graphql/schema.py +2 -1
- nautobot/core/jobs/__init__.py +2 -1
- nautobot/core/settings.py +6 -4
- nautobot/core/settings.yaml +51 -16
- nautobot/core/templates/admin/base.html +2 -2
- nautobot/core/templates/base_django.html +2 -2
- nautobot/core/templates/buttons/export.html +47 -47
- nautobot/core/templates/generic/object_list.html +3 -3
- nautobot/core/templates/inc/javascript.html +3 -0
- nautobot/core/templates/inc/media.html +3 -0
- nautobot/core/templates/login.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +2 -0
- nautobot/core/templatetags/helpers.py +66 -9
- nautobot/core/testing/__init__.py +6 -1
- nautobot/core/testing/api.py +12 -13
- nautobot/core/testing/mixins.py +2 -2
- nautobot/core/testing/views.py +50 -51
- nautobot/core/tests/test_api.py +23 -2
- nautobot/core/tests/test_jobs.py +79 -2
- nautobot/core/tests/test_templatetags_helpers.py +32 -0
- nautobot/core/tests/test_views.py +52 -0
- nautobot/core/tests/test_views_utils.py +22 -1
- nautobot/core/utils/module_loading.py +89 -0
- nautobot/core/views/mixins.py +4 -0
- nautobot/core/views/utils.py +3 -2
- nautobot/dcim/choices.py +14 -0
- nautobot/dcim/forms.py +51 -1
- nautobot/dcim/models/device_components.py +9 -5
- nautobot/dcim/templates/dcim/location.html +32 -13
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +102 -0
- nautobot/dcim/tests/test_views.py +376 -55
- nautobot/dcim/urls.py +5 -0
- nautobot/dcim/views.py +172 -21
- nautobot/extras/api/serializers.py +17 -6
- nautobot/extras/api/views.py +21 -10
- nautobot/extras/constants.py +3 -3
- nautobot/extras/datasources/git.py +47 -58
- nautobot/extras/forms/forms.py +3 -1
- nautobot/extras/jobs.py +79 -146
- nautobot/extras/models/datasources.py +0 -2
- nautobot/extras/models/jobs.py +36 -18
- nautobot/extras/plugins/__init__.py +1 -20
- nautobot/extras/signals.py +6 -9
- nautobot/extras/test_jobs/__init__.py +8 -0
- nautobot/extras/test_jobs/dry_run.py +3 -2
- nautobot/extras/test_jobs/fail.py +43 -0
- nautobot/extras/test_jobs/ipaddress_vars.py +40 -1
- nautobot/extras/test_jobs/jobs_module/__init__.py +5 -0
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/__init__.py +1 -0
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +6 -0
- nautobot/extras/test_jobs/pass.py +40 -0
- nautobot/extras/test_jobs/relative_import.py +11 -0
- nautobot/extras/tests/test_api.py +3 -0
- nautobot/extras/tests/test_context_managers.py +18 -0
- nautobot/extras/tests/test_datasources.py +125 -118
- nautobot/extras/tests/test_job_variables.py +57 -15
- nautobot/extras/tests/test_jobs.py +135 -1
- nautobot/extras/tests/test_models.py +26 -19
- nautobot/extras/tests/test_plugins.py +1 -3
- nautobot/extras/tests/test_views.py +2 -4
- nautobot/extras/utils.py +2 -1
- nautobot/extras/views.py +82 -116
- nautobot/ipam/api/views.py +8 -1
- nautobot/ipam/graphql/types.py +11 -0
- nautobot/ipam/mixins.py +32 -0
- nautobot/ipam/models.py +2 -1
- nautobot/ipam/querysets.py +6 -1
- nautobot/ipam/tests/test_models.py +82 -0
- nautobot/ipam/views.py +6 -6
- nautobot/project-static/docs/404.html +107 -51
- nautobot/project-static/docs/apps/index.html +107 -51
- nautobot/project-static/docs/apps/nautobot-apps.html +107 -51
- nautobot/project-static/docs/assets/_mkdocstrings.css +6 -1
- nautobot/project-static/docs/assets/extra.css +11 -0
- nautobot/project-static/docs/assets/javascripts/bundle.3220b9d7.min.js +29 -0
- nautobot/project-static/docs/assets/javascripts/bundle.3220b9d7.min.js.map +7 -0
- nautobot/project-static/docs/assets/stylesheets/main.66ac8b77.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.66ac8b77.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +108 -52
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +287 -262
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +107 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +107 -51
- nautobot/project-static/docs/development/apps/api/configuration-view.html +110 -54
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +110 -54
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +107 -51
- nautobot/project-static/docs/development/apps/api/models/global-search.html +110 -54
- nautobot/project-static/docs/development/apps/api/models/graphql.html +113 -57
- nautobot/project-static/docs/development/apps/api/models/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +113 -57
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +107 -51
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +107 -51
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +111 -55
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +110 -54
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +107 -51
- nautobot/project-static/docs/development/apps/api/prometheus.html +110 -54
- nautobot/project-static/docs/development/apps/api/setup.html +107 -51
- nautobot/project-static/docs/development/apps/api/testing.html +113 -57
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +110 -54
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +110 -54
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +107 -51
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +113 -57
- nautobot/project-static/docs/development/apps/api/views/base-template.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/index.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +113 -57
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +122 -66
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/notes.html +110 -54
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +107 -51
- nautobot/project-static/docs/development/apps/api/views/urls.html +107 -51
- nautobot/project-static/docs/development/apps/index.html +128 -72
- nautobot/project-static/docs/development/apps/migration/code-updates.html +107 -51
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +107 -51
- nautobot/project-static/docs/development/apps/migration/from-v1.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +107 -51
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +107 -51
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +110 -54
- nautobot/project-static/docs/development/core/application-registry.html +242 -144
- nautobot/project-static/docs/development/core/best-practices.html +122 -66
- nautobot/project-static/docs/development/core/bootstrap-ui.html +107 -51
- nautobot/project-static/docs/development/core/caching.html +107 -51
- nautobot/project-static/docs/development/core/controllers.html +107 -51
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +113 -57
- nautobot/project-static/docs/development/core/generic-views.html +110 -54
- nautobot/project-static/docs/development/core/getting-started.html +135 -79
- nautobot/project-static/docs/development/core/homepage.html +110 -54
- nautobot/project-static/docs/development/core/index.html +107 -51
- nautobot/project-static/docs/development/core/model-checklist.html +156 -52
- nautobot/project-static/docs/development/core/model-features.html +108 -52
- nautobot/project-static/docs/development/core/natural-keys.html +110 -54
- nautobot/project-static/docs/development/core/navigation-menu.html +107 -51
- nautobot/project-static/docs/development/core/release-checklist.html +107 -51
- nautobot/project-static/docs/development/core/role-internals.html +107 -51
- nautobot/project-static/docs/development/core/settings.html +107 -51
- nautobot/project-static/docs/development/core/style-guide.html +110 -54
- nautobot/project-static/docs/development/core/templates.html +113 -57
- nautobot/project-static/docs/development/core/testing.html +125 -69
- nautobot/project-static/docs/development/core/user-preferences.html +107 -51
- nautobot/project-static/docs/development/index.html +107 -51
- nautobot/project-static/docs/development/jobs/index.html +504 -172
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +111 -55
- nautobot/project-static/docs/docker/index.html +3 -3
- nautobot/project-static/docs/index.html +125 -69
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +3 -3
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/index.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.0.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.1.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.2.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.3.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.4.html +107 -51
- nautobot/project-static/docs/release-notes/version-1.5.html +116 -60
- nautobot/project-static/docs/release-notes/version-1.6.html +107 -51
- nautobot/project-static/docs/release-notes/version-2.0.html +110 -54
- nautobot/project-static/docs/release-notes/version-2.1.html +107 -51
- nautobot/project-static/docs/release-notes/version-2.2.html +500 -114
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +262 -262
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +107 -51
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +107 -51
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +107 -51
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +107 -51
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +251 -164
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +113 -57
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +113 -57
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +113 -57
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +107 -51
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +107 -51
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +171 -112
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +13 -8626
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +117 -61
- nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +13 -8614
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +252 -165
- nautobot/project-static/docs/user-guide/administration/installation/index.html +165 -192
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +411 -691
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +248 -229
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +13 -8118
- nautobot/project-static/docs/user-guide/administration/installation/services.html +350 -240
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +8684 -0
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +8672 -0
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +8176 -0
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +110 -54
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +110 -54
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +155 -99
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +109 -53
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +107 -51
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +117 -58
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +116 -60
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +119 -63
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +125 -69
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +128 -72
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +227 -60
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +110 -54
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +107 -51
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +113 -57
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +113 -57
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +113 -57
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +110 -54
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +113 -57
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +107 -51
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +110 -54
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +107 -51
- nautobot/project-static/docs/user-guide/index.html +109 -53
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +113 -57
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +128 -72
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +125 -69
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +125 -69
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +143 -100
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +113 -57
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +113 -57
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +123 -67
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +116 -60
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +131 -75
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +149 -93
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +116 -60
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +119 -63
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +137 -81
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +110 -54
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +107 -51
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +110 -54
- nautobot/project-static/js/forms.js +18 -11
- nautobot/tenancy/views.py +2 -6
- nautobot/virtualization/views.py +5 -9
- {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/METADATA +4 -4
- {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/RECORD +357 -348
- nautobot/extras/test_jobs/job_variables.py +0 -93
- nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js +0 -29
- nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js.map +0 -7
- nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +0 -1
- {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/NOTICE +0 -0
- {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/WHEEL +0 -0
- {nautobot-2.2.2.dist-info → nautobot-2.2.4.dist-info}/entry_points.txt +0 -0
nautobot/dcim/urls.py
CHANGED
|
@@ -84,6 +84,11 @@ urlpatterns = [
|
|
|
84
84
|
name="location_notes",
|
|
85
85
|
kwargs={"model": Location},
|
|
86
86
|
),
|
|
87
|
+
path(
|
|
88
|
+
"locations/<uuid:pk>/migrate-data-to-contact/",
|
|
89
|
+
views.MigrateLocationDataToContactView.as_view(),
|
|
90
|
+
name="location_migrate_data_to_contact",
|
|
91
|
+
),
|
|
87
92
|
path(
|
|
88
93
|
"locations/<uuid:object_id>/images/add/",
|
|
89
94
|
ImageAttachmentEditView.as_view(),
|
nautobot/dcim/views.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from collections import OrderedDict
|
|
2
|
+
import logging
|
|
2
3
|
import uuid
|
|
3
4
|
|
|
4
5
|
from django.contrib import messages
|
|
5
6
|
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
|
6
8
|
from django.core.paginator import EmptyPage, PageNotAnInteger
|
|
7
9
|
from django.db import transaction
|
|
8
10
|
from django.db.models import F, Prefetch
|
|
@@ -12,15 +14,19 @@ from django.forms import (
|
|
|
12
14
|
MultipleHiddenInput,
|
|
13
15
|
)
|
|
14
16
|
from django.shortcuts import get_object_or_404, HttpResponse, redirect, render
|
|
17
|
+
from django.utils.encoding import iri_to_uri
|
|
15
18
|
from django.utils.functional import cached_property
|
|
16
19
|
from django.utils.html import format_html
|
|
20
|
+
from django.utils.http import url_has_allowed_host_and_scheme
|
|
17
21
|
from django.views.generic import View
|
|
18
22
|
from django_tables2 import RequestConfig
|
|
19
23
|
|
|
20
24
|
from nautobot.circuits.models import Circuit
|
|
21
|
-
from nautobot.core.forms import ConfirmationForm
|
|
25
|
+
from nautobot.core.forms import ConfirmationForm, restrict_form_fields
|
|
22
26
|
from nautobot.core.models.querysets import count_related
|
|
27
|
+
from nautobot.core.templatetags.helpers import has_perms
|
|
23
28
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
29
|
+
from nautobot.core.utils.requests import normalize_querydict
|
|
24
30
|
from nautobot.core.views import generic
|
|
25
31
|
from nautobot.core.views.mixins import (
|
|
26
32
|
GetReturnURLMixin,
|
|
@@ -30,7 +36,10 @@ from nautobot.core.views.mixins import (
|
|
|
30
36
|
)
|
|
31
37
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
32
38
|
from nautobot.core.views.viewsets import NautobotUIViewSet
|
|
39
|
+
from nautobot.dcim.choices import LocationDataToContactActionChoices
|
|
40
|
+
from nautobot.dcim.forms import LocationMigrateDataToContactForm
|
|
33
41
|
from nautobot.dcim.utils import get_all_network_driver_mappings, get_network_driver_mapping_tool_names
|
|
42
|
+
from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
|
|
34
43
|
from nautobot.extras.views import ObjectChangeLogView, ObjectConfigContextView, ObjectDynamicGroupsView
|
|
35
44
|
from nautobot.ipam.models import IPAddress, Prefix, Service, VLAN
|
|
36
45
|
from nautobot.ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable, VRFDeviceAssignmentTable
|
|
@@ -83,6 +92,8 @@ from .models import (
|
|
|
83
92
|
VirtualChassis,
|
|
84
93
|
)
|
|
85
94
|
|
|
95
|
+
logger = logging.getLogger(__name__)
|
|
96
|
+
|
|
86
97
|
|
|
87
98
|
class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
88
99
|
"""
|
|
@@ -188,6 +199,7 @@ class LocationTypeView(generic.ObjectView):
|
|
|
188
199
|
return {
|
|
189
200
|
"children_table": children_table,
|
|
190
201
|
"locations_table": locations_table,
|
|
202
|
+
**super().get_extra_context(request, instance),
|
|
191
203
|
}
|
|
192
204
|
|
|
193
205
|
|
|
@@ -283,6 +295,10 @@ class LocationView(generic.ObjectView):
|
|
|
283
295
|
"children_table": children_table,
|
|
284
296
|
"rack_groups": rack_groups,
|
|
285
297
|
"stats": stats,
|
|
298
|
+
"contact_association_permission": ["extras.add_contactassociation"],
|
|
299
|
+
# show the button if any of these fields have non-empty value.
|
|
300
|
+
"show_convert_to_contact_button": instance.contact_name or instance.contact_phone or instance.contact_email,
|
|
301
|
+
**super().get_extra_context(request, instance),
|
|
286
302
|
}
|
|
287
303
|
|
|
288
304
|
|
|
@@ -314,6 +330,140 @@ class LocationBulkDeleteView(generic.BulkDeleteView):
|
|
|
314
330
|
table = tables.LocationTable
|
|
315
331
|
|
|
316
332
|
|
|
333
|
+
class MigrateLocationDataToContactView(generic.ObjectEditView):
|
|
334
|
+
queryset = Location.objects.all()
|
|
335
|
+
model_form = LocationMigrateDataToContactForm
|
|
336
|
+
template_name = "dcim/location_migrate_data_to_contact.html"
|
|
337
|
+
|
|
338
|
+
def get(self, request, *args, **kwargs):
|
|
339
|
+
obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
|
|
340
|
+
|
|
341
|
+
initial_data = normalize_querydict(request.GET, form_class=self.model_form)
|
|
342
|
+
# remove status from the location itself
|
|
343
|
+
initial_data["status"] = None
|
|
344
|
+
initial_data["location"] = obj.pk
|
|
345
|
+
|
|
346
|
+
# populate contact tab fields initial data
|
|
347
|
+
initial_data["name"] = obj.contact_name
|
|
348
|
+
initial_data["phone"] = obj.contact_phone
|
|
349
|
+
initial_data["email"] = obj.contact_email
|
|
350
|
+
form = self.model_form(instance=obj, initial=initial_data)
|
|
351
|
+
restrict_form_fields(form, request.user)
|
|
352
|
+
return render(
|
|
353
|
+
request,
|
|
354
|
+
self.template_name,
|
|
355
|
+
{
|
|
356
|
+
"obj": obj,
|
|
357
|
+
"obj_type": self.queryset.model._meta.verbose_name,
|
|
358
|
+
"form": form,
|
|
359
|
+
"return_url": self.get_return_url(request, obj),
|
|
360
|
+
"editing": obj.present_in_database,
|
|
361
|
+
"active_tab": "assign",
|
|
362
|
+
**self.get_extra_context(request, obj),
|
|
363
|
+
},
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
def post(self, request, *args, **kwargs):
|
|
367
|
+
obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
|
|
368
|
+
form = self.model_form(data=request.POST, files=request.FILES, instance=obj)
|
|
369
|
+
restrict_form_fields(form, request.user)
|
|
370
|
+
|
|
371
|
+
associated_object_id = obj.pk
|
|
372
|
+
associated_object_content_type = ContentType.objects.get_for_model(Location)
|
|
373
|
+
action = request.POST.get("action")
|
|
374
|
+
try:
|
|
375
|
+
with transaction.atomic():
|
|
376
|
+
if not has_perms(request.user, ["extras.add_contactassociation"]):
|
|
377
|
+
raise PermissionDenied(
|
|
378
|
+
"ObjectPermission extras.add_contactassociation is needed to perform this action"
|
|
379
|
+
)
|
|
380
|
+
contact = None
|
|
381
|
+
team = None
|
|
382
|
+
if action == LocationDataToContactActionChoices.CREATE_AND_ASSIGN_NEW_CONTACT:
|
|
383
|
+
if not has_perms(request.user, ["extras.add_contact"]):
|
|
384
|
+
raise PermissionDenied("ObjectPermission extras.add_contact is needed to perform this action")
|
|
385
|
+
contact = Contact(
|
|
386
|
+
name=request.POST.get("name"),
|
|
387
|
+
phone=request.POST.get("phone"),
|
|
388
|
+
email=request.POST.get("email"),
|
|
389
|
+
)
|
|
390
|
+
contact.validated_save()
|
|
391
|
+
# Trigger permission check
|
|
392
|
+
Contact.objects.restrict(request.user, "view").get(pk=contact.pk)
|
|
393
|
+
elif action == LocationDataToContactActionChoices.CREATE_AND_ASSIGN_NEW_TEAM:
|
|
394
|
+
if not has_perms(request.user, ["extras.add_team"]):
|
|
395
|
+
raise PermissionDenied("ObjectPermission extras.add_team is needed to perform this action")
|
|
396
|
+
team = Team(
|
|
397
|
+
name=request.POST.get("name"),
|
|
398
|
+
phone=request.POST.get("phone"),
|
|
399
|
+
email=request.POST.get("email"),
|
|
400
|
+
)
|
|
401
|
+
team.validated_save()
|
|
402
|
+
# Trigger permission check
|
|
403
|
+
Team.objects.restrict(request.user, "view").get(pk=team.pk)
|
|
404
|
+
elif action == LocationDataToContactActionChoices.ASSOCIATE_EXISTING_CONTACT:
|
|
405
|
+
contact = Contact.objects.restrict(request.user, "view").get(pk=request.POST.get("contact"))
|
|
406
|
+
elif action == LocationDataToContactActionChoices.ASSOCIATE_EXISTING_TEAM:
|
|
407
|
+
team = Team.objects.restrict(request.user, "view").get(pk=request.POST.get("team"))
|
|
408
|
+
else:
|
|
409
|
+
raise ValueError(f"Invalid action {action} passed from the form")
|
|
410
|
+
|
|
411
|
+
association = ContactAssociation(
|
|
412
|
+
contact=contact,
|
|
413
|
+
team=team,
|
|
414
|
+
associated_object_type=associated_object_content_type,
|
|
415
|
+
associated_object_id=associated_object_id,
|
|
416
|
+
status=Status.objects.get(pk=request.POST.get("status")),
|
|
417
|
+
role=Role.objects.get(pk=request.POST.get("role")),
|
|
418
|
+
)
|
|
419
|
+
association.validated_save()
|
|
420
|
+
# Trigger permission check
|
|
421
|
+
ContactAssociation.objects.restrict(request.user, "view").get(pk=association.pk)
|
|
422
|
+
|
|
423
|
+
# Clear out contact fields from location
|
|
424
|
+
location = self.get_object(kwargs)
|
|
425
|
+
location.contact_name = ""
|
|
426
|
+
location.contact_phone = ""
|
|
427
|
+
location.contact_email = ""
|
|
428
|
+
location.validated_save()
|
|
429
|
+
|
|
430
|
+
object_created = not form.instance.present_in_database
|
|
431
|
+
|
|
432
|
+
self.successful_post(request, obj, object_created, logger)
|
|
433
|
+
|
|
434
|
+
return_url = request.POST.get("return_url")
|
|
435
|
+
if url_has_allowed_host_and_scheme(url=return_url, allowed_hosts=request.get_host()):
|
|
436
|
+
return redirect(iri_to_uri(return_url))
|
|
437
|
+
else:
|
|
438
|
+
return redirect(self.get_return_url(request, obj))
|
|
439
|
+
|
|
440
|
+
except ObjectDoesNotExist:
|
|
441
|
+
msg = "Object save failed due to object-level permissions violation"
|
|
442
|
+
logger.debug(msg)
|
|
443
|
+
form.add_error(None, msg)
|
|
444
|
+
except PermissionDenied as e:
|
|
445
|
+
msg = e
|
|
446
|
+
logger.debug(msg)
|
|
447
|
+
form.add_error(None, msg)
|
|
448
|
+
except ValueError:
|
|
449
|
+
msg = f"Invalid action {action} passed from the form"
|
|
450
|
+
logger.debug(msg)
|
|
451
|
+
form.add_error(None, msg)
|
|
452
|
+
|
|
453
|
+
return render(
|
|
454
|
+
request,
|
|
455
|
+
self.template_name,
|
|
456
|
+
{
|
|
457
|
+
"obj": obj,
|
|
458
|
+
"obj_type": self.queryset.model._meta.verbose_name,
|
|
459
|
+
"form": form,
|
|
460
|
+
"return_url": self.get_return_url(request, obj),
|
|
461
|
+
"editing": obj.present_in_database,
|
|
462
|
+
**self.get_extra_context(request, obj),
|
|
463
|
+
},
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
|
|
317
467
|
#
|
|
318
468
|
# Rack groups
|
|
319
469
|
#
|
|
@@ -346,9 +496,7 @@ class RackGroupView(generic.ObjectView):
|
|
|
346
496
|
}
|
|
347
497
|
RequestConfig(request, paginate).configure(rack_table)
|
|
348
498
|
|
|
349
|
-
return {
|
|
350
|
-
"rack_table": rack_table,
|
|
351
|
-
}
|
|
499
|
+
return {"rack_table": rack_table, **super().get_extra_context(request, instance)}
|
|
352
500
|
|
|
353
501
|
|
|
354
502
|
class RackGroupEditView(generic.ObjectEditView):
|
|
@@ -467,6 +615,7 @@ class RackView(generic.ObjectView):
|
|
|
467
615
|
"nonracked_devices": nonracked_devices,
|
|
468
616
|
"next_rack": next_rack,
|
|
469
617
|
"prev_rack": prev_rack,
|
|
618
|
+
**super().get_extra_context(request, instance),
|
|
470
619
|
}
|
|
471
620
|
|
|
472
621
|
|
|
@@ -583,9 +732,7 @@ class ManufacturerView(generic.ObjectView):
|
|
|
583
732
|
}
|
|
584
733
|
RequestConfig(request, paginate).configure(device_table)
|
|
585
734
|
|
|
586
|
-
return {
|
|
587
|
-
"device_table": device_table,
|
|
588
|
-
}
|
|
735
|
+
return {"device_table": device_table, **super().get_extra_context(request, instance)}
|
|
589
736
|
|
|
590
737
|
|
|
591
738
|
class ManufacturerEditView(generic.ObjectEditView):
|
|
@@ -691,6 +838,7 @@ class DeviceTypeView(generic.ObjectView):
|
|
|
691
838
|
"rear_port_table": rear_port_table,
|
|
692
839
|
"devicebay_table": devicebay_table,
|
|
693
840
|
"software_image_files_table": software_image_files_table,
|
|
841
|
+
**super().get_extra_context(request, instance),
|
|
694
842
|
}
|
|
695
843
|
|
|
696
844
|
|
|
@@ -1089,6 +1237,7 @@ class PlatformView(generic.ObjectView):
|
|
|
1089
1237
|
return {
|
|
1090
1238
|
"device_table": device_table,
|
|
1091
1239
|
"network_driver_tool_names": get_network_driver_mapping_tool_names(),
|
|
1240
|
+
**super().get_extra_context(request, instance),
|
|
1092
1241
|
}
|
|
1093
1242
|
|
|
1094
1243
|
|
|
@@ -1098,7 +1247,10 @@ class PlatformEditView(generic.ObjectEditView):
|
|
|
1098
1247
|
template_name = "dcim/platform_edit.html"
|
|
1099
1248
|
|
|
1100
1249
|
def get_extra_context(self, request, instance):
|
|
1101
|
-
return {
|
|
1250
|
+
return {
|
|
1251
|
+
"network_driver_names": sorted(get_all_network_driver_mappings().keys()),
|
|
1252
|
+
**super().get_extra_context(request, instance),
|
|
1253
|
+
}
|
|
1102
1254
|
|
|
1103
1255
|
|
|
1104
1256
|
class PlatformDeleteView(generic.ObjectDeleteView):
|
|
@@ -1489,7 +1641,7 @@ class ConsolePortView(generic.ObjectView):
|
|
|
1489
1641
|
queryset = ConsolePort.objects.all()
|
|
1490
1642
|
|
|
1491
1643
|
def get_extra_context(self, request, instance):
|
|
1492
|
-
return {"breadcrumb_url": "dcim:device_consoleports"}
|
|
1644
|
+
return {"breadcrumb_url": "dcim:device_consoleports", **super().get_extra_context(request, instance)}
|
|
1493
1645
|
|
|
1494
1646
|
|
|
1495
1647
|
class ConsolePortCreateView(generic.ComponentCreateView):
|
|
@@ -1551,7 +1703,7 @@ class ConsoleServerPortView(generic.ObjectView):
|
|
|
1551
1703
|
queryset = ConsoleServerPort.objects.all()
|
|
1552
1704
|
|
|
1553
1705
|
def get_extra_context(self, request, instance):
|
|
1554
|
-
return {"breadcrumb_url": "dcim:device_consoleserverports"}
|
|
1706
|
+
return {"breadcrumb_url": "dcim:device_consoleserverports", **super().get_extra_context(request, instance)}
|
|
1555
1707
|
|
|
1556
1708
|
|
|
1557
1709
|
class ConsoleServerPortCreateView(generic.ComponentCreateView):
|
|
@@ -1613,7 +1765,7 @@ class PowerPortView(generic.ObjectView):
|
|
|
1613
1765
|
queryset = PowerPort.objects.all()
|
|
1614
1766
|
|
|
1615
1767
|
def get_extra_context(self, request, instance):
|
|
1616
|
-
return {"breadcrumb_url": "dcim:device_powerports"}
|
|
1768
|
+
return {"breadcrumb_url": "dcim:device_powerports", **super().get_extra_context(request, instance)}
|
|
1617
1769
|
|
|
1618
1770
|
|
|
1619
1771
|
class PowerPortCreateView(generic.ComponentCreateView):
|
|
@@ -1675,7 +1827,7 @@ class PowerOutletView(generic.ObjectView):
|
|
|
1675
1827
|
queryset = PowerOutlet.objects.all()
|
|
1676
1828
|
|
|
1677
1829
|
def get_extra_context(self, request, instance):
|
|
1678
|
-
return {"breadcrumb_url": "dcim:device_poweroutlets"}
|
|
1830
|
+
return {"breadcrumb_url": "dcim:device_poweroutlets", **super().get_extra_context(request, instance)}
|
|
1679
1831
|
|
|
1680
1832
|
|
|
1681
1833
|
class PowerOutletCreateView(generic.ComponentCreateView):
|
|
@@ -1771,6 +1923,7 @@ class InterfaceView(generic.ObjectView):
|
|
|
1771
1923
|
"breadcrumb_url": "dcim:device_interfaces",
|
|
1772
1924
|
"child_interfaces_table": child_interfaces_tables,
|
|
1773
1925
|
"redundancy_table": redundancy_table,
|
|
1926
|
+
**super().get_extra_context(request, instance),
|
|
1774
1927
|
}
|
|
1775
1928
|
|
|
1776
1929
|
def _get_interface_redundancy_groups_table(self, request, instance):
|
|
@@ -1857,7 +2010,7 @@ class FrontPortView(generic.ObjectView):
|
|
|
1857
2010
|
queryset = FrontPort.objects.all()
|
|
1858
2011
|
|
|
1859
2012
|
def get_extra_context(self, request, instance):
|
|
1860
|
-
return {"breadcrumb_url": "dcim:device_frontports"}
|
|
2013
|
+
return {"breadcrumb_url": "dcim:device_frontports", **super().get_extra_context(request, instance)}
|
|
1861
2014
|
|
|
1862
2015
|
|
|
1863
2016
|
class FrontPortCreateView(generic.ComponentCreateView):
|
|
@@ -1919,7 +2072,7 @@ class RearPortView(generic.ObjectView):
|
|
|
1919
2072
|
queryset = RearPort.objects.all()
|
|
1920
2073
|
|
|
1921
2074
|
def get_extra_context(self, request, instance):
|
|
1922
|
-
return {"breadcrumb_url": "dcim:device_rearports"}
|
|
2075
|
+
return {"breadcrumb_url": "dcim:device_rearports", **super().get_extra_context(request, instance)}
|
|
1923
2076
|
|
|
1924
2077
|
|
|
1925
2078
|
class RearPortCreateView(generic.ComponentCreateView):
|
|
@@ -1981,7 +2134,7 @@ class DeviceBayView(generic.ObjectView):
|
|
|
1981
2134
|
queryset = DeviceBay.objects.all()
|
|
1982
2135
|
|
|
1983
2136
|
def get_extra_context(self, request, instance):
|
|
1984
|
-
return {"breadcrumb_url": "dcim:device_devicebays"}
|
|
2137
|
+
return {"breadcrumb_url": "dcim:device_devicebays", **super().get_extra_context(request, instance)}
|
|
1985
2138
|
|
|
1986
2139
|
|
|
1987
2140
|
class DeviceBayCreateView(generic.ComponentCreateView):
|
|
@@ -2133,6 +2286,7 @@ class InventoryItemView(generic.ObjectView):
|
|
|
2133
2286
|
return {
|
|
2134
2287
|
"breadcrumb_url": "dcim:device_inventory",
|
|
2135
2288
|
"software_version_images": software_version_images,
|
|
2289
|
+
**super().get_extra_context(request, instance),
|
|
2136
2290
|
}
|
|
2137
2291
|
|
|
2138
2292
|
|
|
@@ -2340,6 +2494,7 @@ class PathTraceView(generic.ObjectView):
|
|
|
2340
2494
|
"path": path,
|
|
2341
2495
|
"related_paths": related_paths,
|
|
2342
2496
|
"total_length": path.get_total_length() if path else None,
|
|
2497
|
+
**super().get_extra_context(request, instance),
|
|
2343
2498
|
}
|
|
2344
2499
|
|
|
2345
2500
|
|
|
@@ -2550,9 +2705,7 @@ class VirtualChassisView(generic.ObjectView):
|
|
|
2550
2705
|
def get_extra_context(self, request, instance):
|
|
2551
2706
|
members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
|
|
2552
2707
|
|
|
2553
|
-
return {
|
|
2554
|
-
"members": members,
|
|
2555
|
-
}
|
|
2708
|
+
return {"members": members, **super().get_extra_context(request, instance)}
|
|
2556
2709
|
|
|
2557
2710
|
|
|
2558
2711
|
class VirtualChassisCreateView(generic.ObjectEditView):
|
|
@@ -2790,9 +2943,7 @@ class PowerPanelView(generic.ObjectView):
|
|
|
2790
2943
|
powerfeed_table = tables.PowerFeedTable(data=power_feeds, orderable=False)
|
|
2791
2944
|
powerfeed_table.exclude = ["power_panel"]
|
|
2792
2945
|
|
|
2793
|
-
return {
|
|
2794
|
-
"powerfeed_table": powerfeed_table,
|
|
2795
|
-
}
|
|
2946
|
+
return {"powerfeed_table": powerfeed_table, **super().get_extra_context(request, instance)}
|
|
2796
2947
|
|
|
2797
2948
|
|
|
2798
2949
|
class PowerPanelEditView(generic.ObjectEditView):
|
|
@@ -223,17 +223,28 @@ class ContactAssociationSerializer(NautobotModelSerializer):
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
def validate(self, data):
|
|
226
|
-
# Validate uniqueness of (contact/team, role)
|
|
226
|
+
# Validate uniqueness of (associated object, associated object type, contact/team, role)
|
|
227
|
+
unique_together_fields = None
|
|
228
|
+
|
|
227
229
|
if data.get("contact") and data.get("role"):
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
230
|
+
unique_together_fields = (
|
|
231
|
+
"associated_object_type",
|
|
232
|
+
"associated_object_id",
|
|
233
|
+
"contact",
|
|
234
|
+
"role",
|
|
231
235
|
)
|
|
232
|
-
validator(data, self)
|
|
233
236
|
elif data.get("team") and data.get("role"):
|
|
237
|
+
unique_together_fields = (
|
|
238
|
+
"associated_object_type",
|
|
239
|
+
"associated_object_id",
|
|
240
|
+
"team",
|
|
241
|
+
"role",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if unique_together_fields is not None:
|
|
234
245
|
validator = UniqueTogetherValidator(
|
|
235
246
|
queryset=ContactAssociation.objects.all(),
|
|
236
|
-
fields=
|
|
247
|
+
fields=unique_together_fields,
|
|
237
248
|
)
|
|
238
249
|
validator(data, self)
|
|
239
250
|
|
nautobot/extras/api/views.py
CHANGED
|
@@ -33,6 +33,7 @@ from nautobot.core.models.querysets import count_related
|
|
|
33
33
|
from nautobot.extras import filters
|
|
34
34
|
from nautobot.extras.choices import JobExecutionType
|
|
35
35
|
from nautobot.extras.filters import RoleFilterSet
|
|
36
|
+
from nautobot.extras.jobs import get_job
|
|
36
37
|
from nautobot.extras.models import (
|
|
37
38
|
ComputedField,
|
|
38
39
|
ConfigContext,
|
|
@@ -484,7 +485,7 @@ def _create_schedule(serializer, data, job_model, user, approval_required, task_
|
|
|
484
485
|
# scheduled for.
|
|
485
486
|
scheduled_job = ScheduledJob(
|
|
486
487
|
name=name,
|
|
487
|
-
task=job_model.
|
|
488
|
+
task=job_model.class_path,
|
|
488
489
|
job_model=job_model,
|
|
489
490
|
start_time=time,
|
|
490
491
|
description=f"Nautobot job {name} scheduled by {user} for {time}",
|
|
@@ -520,7 +521,7 @@ class JobViewSetBase(
|
|
|
520
521
|
def variables(self, request, *args, **kwargs):
|
|
521
522
|
"""Get details of the input variables that may/must be specified to run a particular Job."""
|
|
522
523
|
job_model = self.get_object()
|
|
523
|
-
job_class = job_model.
|
|
524
|
+
job_class = get_job(job_model.class_path, reload=True)
|
|
524
525
|
if job_class is None:
|
|
525
526
|
raise Http404
|
|
526
527
|
variables_dict = job_class._get_vars()
|
|
@@ -602,14 +603,15 @@ class JobViewSetBase(
|
|
|
602
603
|
"One of these two flags must be removed before this job can be scheduled or run."
|
|
603
604
|
)
|
|
604
605
|
|
|
605
|
-
job_class = job_model.
|
|
606
|
+
job_class = get_job(job_model.class_path, reload=True)
|
|
606
607
|
if job_class is None:
|
|
607
608
|
raise MethodNotAllowed(
|
|
608
609
|
request.method, detail="This job's source code could not be located and cannot be run"
|
|
609
610
|
)
|
|
610
611
|
|
|
611
612
|
valid_queues = job_model.task_queues if job_model.task_queues else [settings.CELERY_TASK_DEFAULT_QUEUE]
|
|
612
|
-
# Get a default queue from either the job model's specified task queue or
|
|
613
|
+
# Get a default queue from either the job model's specified task queue or
|
|
614
|
+
# the system default to fall back on if request doesn't provide one
|
|
613
615
|
default_valid_queue = valid_queues[0]
|
|
614
616
|
|
|
615
617
|
# We need to call request.data for both cases as this is what pulls and caches the request data
|
|
@@ -621,7 +623,8 @@ class JobViewSetBase(
|
|
|
621
623
|
# - Job Form data (for submission to the job itself)
|
|
622
624
|
# - Schedule data
|
|
623
625
|
# - Desired task queue
|
|
624
|
-
# Depending on request content type (largely for backwards compatibility) the keys at which these are found
|
|
626
|
+
# Depending on request content type (largely for backwards compatibility) the keys at which these are found
|
|
627
|
+
# are different
|
|
625
628
|
if "multipart/form-data" in request.content_type:
|
|
626
629
|
data = request._data.dict() # .data will return data and files, we just want the data
|
|
627
630
|
files = request.FILES
|
|
@@ -639,7 +642,8 @@ class JobViewSetBase(
|
|
|
639
642
|
for non_job_key in non_job_keys:
|
|
640
643
|
data.pop(non_job_key, None)
|
|
641
644
|
|
|
642
|
-
# List of keys in serializer that are effectively exploded versions of the schedule dictionary
|
|
645
|
+
# List of keys in serializer that are effectively exploded versions of the schedule dictionary
|
|
646
|
+
# from JobInputSerializer
|
|
643
647
|
schedule_keys = ("_schedule_name", "_schedule_start_time", "_schedule_interval", "_schedule_crontab")
|
|
644
648
|
|
|
645
649
|
# Assign the key from the validated_data output to dictionary without prefixed "_schedule_"
|
|
@@ -666,7 +670,7 @@ class JobViewSetBase(
|
|
|
666
670
|
cleaned_data = None
|
|
667
671
|
try:
|
|
668
672
|
cleaned_data = job_class.validate_data(data, files=files)
|
|
669
|
-
cleaned_data =
|
|
673
|
+
cleaned_data = job_class.prepare_job_kwargs(cleaned_data)
|
|
670
674
|
|
|
671
675
|
except FormsValidationError as e:
|
|
672
676
|
# message_dict can only be accessed if ValidationError got a dict
|
|
@@ -948,7 +952,13 @@ class ScheduledJobViewSet(ReadOnlyModelViewSet):
|
|
|
948
952
|
responses={"200": serializers.JobResultSerializer},
|
|
949
953
|
request=None,
|
|
950
954
|
)
|
|
951
|
-
@action(
|
|
955
|
+
@action(
|
|
956
|
+
detail=True,
|
|
957
|
+
name="Dry Run",
|
|
958
|
+
url_path="dry-run",
|
|
959
|
+
methods=["post"],
|
|
960
|
+
permission_classes=[ScheduledJobViewPermissions],
|
|
961
|
+
)
|
|
952
962
|
def dry_run(self, request, pk):
|
|
953
963
|
scheduled_job = get_object_or_404(ScheduledJob, pk=pk)
|
|
954
964
|
job_model = scheduled_job.job_model
|
|
@@ -960,13 +970,14 @@ class ScheduledJobViewSet(ReadOnlyModelViewSet):
|
|
|
960
970
|
raise PermissionDenied("You do not have permission to run this job.")
|
|
961
971
|
|
|
962
972
|
# Immediately enqueue the job
|
|
963
|
-
|
|
973
|
+
job_class = get_job(job_model.class_path, reload=True)
|
|
974
|
+
job_kwargs = job_class.prepare_job_kwargs(scheduled_job.kwargs or {})
|
|
964
975
|
job_kwargs["dryrun"] = True
|
|
965
976
|
job_result = JobResult.enqueue_job(
|
|
966
977
|
job_model,
|
|
967
978
|
request.user,
|
|
968
979
|
celery_kwargs=scheduled_job.celery_kwargs or {},
|
|
969
|
-
**
|
|
980
|
+
**job_class.serialize_data(job_kwargs),
|
|
970
981
|
)
|
|
971
982
|
serializer = serializers.JobResultSerializer(job_result, context={"request": request})
|
|
972
983
|
|
nautobot/extras/constants.py
CHANGED
|
@@ -5,16 +5,16 @@ HTTP_CONTENT_TYPE_JSON = "application/json"
|
|
|
5
5
|
EXTRAS_FEATURES = [
|
|
6
6
|
"cable_terminations",
|
|
7
7
|
"config_context_owners",
|
|
8
|
-
"custom_fields",
|
|
8
|
+
"custom_fields", # Deprecated - see nautobot.extras.utils.populate_model_features_registry
|
|
9
9
|
"custom_links",
|
|
10
10
|
"custom_validators",
|
|
11
11
|
"dynamic_groups",
|
|
12
12
|
"export_template_owners",
|
|
13
13
|
"export_templates",
|
|
14
14
|
"graphql",
|
|
15
|
-
"job_results",
|
|
15
|
+
"job_results", # No longer used
|
|
16
16
|
"locations",
|
|
17
|
-
"relationships",
|
|
17
|
+
"relationships", # Deprecated - see nautobot.extras.utils.populate_model_features_registry
|
|
18
18
|
"statuses",
|
|
19
19
|
"webhooks",
|
|
20
20
|
]
|