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
|
@@ -80,9 +80,10 @@ class GitTest(TransactionTestCase):
|
|
|
80
80
|
status=status,
|
|
81
81
|
)
|
|
82
82
|
|
|
83
|
+
self.repo_slug = "test_git_repo"
|
|
83
84
|
self.repo = GitRepository(
|
|
84
85
|
name="Test Git Repository",
|
|
85
|
-
slug=
|
|
86
|
+
slug=self.repo_slug,
|
|
86
87
|
remote_url="http://localhost/git.git",
|
|
87
88
|
# Provide everything we know we can provide
|
|
88
89
|
provided_contents=[entry.content_identifier for entry in get_datasource_contents("extras.gitrepository")],
|
|
@@ -118,22 +119,24 @@ class GitTest(TransactionTestCase):
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
def tearDown(self):
|
|
121
|
-
if f"{self.
|
|
122
|
-
del sys.modules[f"{self.
|
|
123
|
-
if f"{self.
|
|
124
|
-
del sys.modules[f"{self.
|
|
122
|
+
if f"{self.repo_slug}.jobs" in sys.modules:
|
|
123
|
+
del sys.modules[f"{self.repo_slug}.jobs"]
|
|
124
|
+
if f"{self.repo_slug}" in sys.modules:
|
|
125
|
+
del sys.modules[f"{self.repo_slug}"]
|
|
126
|
+
if self.repo is not None:
|
|
127
|
+
self.repo.delete()
|
|
125
128
|
super().tearDown()
|
|
126
129
|
|
|
127
130
|
def populate_repo(self, path, url, *args, **kwargs):
|
|
128
|
-
os.makedirs(path)
|
|
131
|
+
os.makedirs(path, exist_ok=True)
|
|
129
132
|
|
|
130
|
-
os.makedirs(os.path.join(path, "config_contexts"))
|
|
131
|
-
os.makedirs(os.path.join(path, "config_contexts", "devices"))
|
|
132
|
-
os.makedirs(os.path.join(path, "config_contexts", "locations"))
|
|
133
|
-
os.makedirs(os.path.join(path, "config_context_schemas"))
|
|
134
|
-
os.makedirs(os.path.join(path, "export_templates", "dcim", "device"))
|
|
135
|
-
os.makedirs(os.path.join(path, "export_templates", "ipam", "vlan"))
|
|
136
|
-
os.makedirs(os.path.join(path, "jobs"))
|
|
133
|
+
os.makedirs(os.path.join(path, "config_contexts"), exist_ok=True)
|
|
134
|
+
os.makedirs(os.path.join(path, "config_contexts", "devices"), exist_ok=True)
|
|
135
|
+
os.makedirs(os.path.join(path, "config_contexts", "locations"), exist_ok=True)
|
|
136
|
+
os.makedirs(os.path.join(path, "config_context_schemas"), exist_ok=True)
|
|
137
|
+
os.makedirs(os.path.join(path, "export_templates", "dcim", "device"), exist_ok=True)
|
|
138
|
+
os.makedirs(os.path.join(path, "export_templates", "ipam", "vlan"), exist_ok=True)
|
|
139
|
+
os.makedirs(os.path.join(path, "jobs"), exist_ok=True)
|
|
137
140
|
|
|
138
141
|
with open(os.path.join(path, "__init__.py"), "w") as fd:
|
|
139
142
|
# Required for job importing
|
|
@@ -188,16 +191,19 @@ class GitTest(TransactionTestCase):
|
|
|
188
191
|
return mock.DEFAULT
|
|
189
192
|
|
|
190
193
|
def empty_repo(self, path, url, *args, **kwargs):
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
try:
|
|
195
|
+
os.remove(os.path.join(path, "__init__.py"))
|
|
196
|
+
os.remove(os.path.join(path, "config_contexts", "context.yaml"))
|
|
197
|
+
os.remove(os.path.join(path, "config_contexts", "locations", f"{self.location.name}.json"))
|
|
198
|
+
os.remove(os.path.join(path, "config_contexts", "devices", f"{self.device.name}.json"))
|
|
199
|
+
os.remove(os.path.join(path, "config_context_schemas", "schema-1.yaml"))
|
|
200
|
+
os.remove(os.path.join(path, "export_templates", "dcim", "device", "template.j2"))
|
|
201
|
+
os.remove(os.path.join(path, "export_templates", "dcim", "device", "template2.html"))
|
|
202
|
+
os.remove(os.path.join(path, "export_templates", "ipam", "vlan", "template.j2"))
|
|
203
|
+
os.remove(os.path.join(path, "jobs", "__init__.py"))
|
|
204
|
+
os.remove(os.path.join(path, "jobs", "my_job.py"))
|
|
205
|
+
except FileNotFoundError:
|
|
206
|
+
pass
|
|
201
207
|
return mock.DEFAULT
|
|
202
208
|
|
|
203
209
|
def assert_repo_slug_valid_python_package_name(self):
|
|
@@ -291,15 +297,14 @@ class GitTest(TransactionTestCase):
|
|
|
291
297
|
self.assertIsNotNone(export_template_vlan)
|
|
292
298
|
|
|
293
299
|
def assert_job_exists(self, installed=True):
|
|
294
|
-
"""Helper function to assert JobModel and
|
|
300
|
+
"""Helper function to assert JobModel and registered Job exist."""
|
|
295
301
|
# Is it registered correctly in the database?
|
|
296
|
-
job_model = Job.objects.get(name="MyJob", module_name=f"{self.
|
|
302
|
+
job_model = Job.objects.get(name="MyJob", module_name=f"{self.repo_slug}.jobs.my_job", job_class_name="MyJob")
|
|
297
303
|
self.assertIsNotNone(job_model)
|
|
298
304
|
if installed:
|
|
299
305
|
self.assertTrue(job_model.installed)
|
|
300
306
|
# Is the in-memory code accessible?
|
|
301
307
|
self.assertIsNotNone(job_model.job_class)
|
|
302
|
-
# Is it registered properly with Celery?
|
|
303
308
|
self.assertIsNotNone(job_model.job_task)
|
|
304
309
|
else:
|
|
305
310
|
self.assertFalse(job_model.installed)
|
|
@@ -333,10 +338,7 @@ class GitTest(TransactionTestCase):
|
|
|
333
338
|
self.assertEqual(
|
|
334
339
|
job_result.status,
|
|
335
340
|
JobResultStatusChoices.STATUS_FAILURE,
|
|
336
|
-
(
|
|
337
|
-
job_result.result,
|
|
338
|
-
list(job_result.job_log_entries.filter(log_level="error").values_list("message", flat=True)),
|
|
339
|
-
),
|
|
341
|
+
(job_result.result, list(job_result.job_log_entries.values_list("message", "log_object"))),
|
|
340
342
|
)
|
|
341
343
|
self.repo.refresh_from_db()
|
|
342
344
|
self.assertEqual(self.repo.current_head, self.COMMIT_HEXSHA, job_result.result)
|
|
@@ -344,7 +346,13 @@ class GitTest(TransactionTestCase):
|
|
|
344
346
|
|
|
345
347
|
log_entries = JobLogEntry.objects.filter(job_result=job_result)
|
|
346
348
|
failure_logs = log_entries.filter(log_level=LogLevelChoices.LOG_ERROR)
|
|
347
|
-
|
|
349
|
+
try:
|
|
350
|
+
failure_logs.get(grouping="jobs", message__contains="No `jobs` submodule found")
|
|
351
|
+
except JobLogEntry.DoesNotExist:
|
|
352
|
+
for log in log_entries:
|
|
353
|
+
print(log.message)
|
|
354
|
+
print(job_result.traceback)
|
|
355
|
+
raise
|
|
348
356
|
|
|
349
357
|
def test_pull_git_repository_and_refresh_data_with_secrets(self, MockGitRepo):
|
|
350
358
|
"""
|
|
@@ -406,10 +414,7 @@ class GitTest(TransactionTestCase):
|
|
|
406
414
|
self.assertEqual(
|
|
407
415
|
job_result.status,
|
|
408
416
|
JobResultStatusChoices.STATUS_SUCCESS,
|
|
409
|
-
(
|
|
410
|
-
job_result.result,
|
|
411
|
-
list(job_result.job_log_entries.filter(log_level="error").values_list("message", flat=True)),
|
|
412
|
-
),
|
|
417
|
+
(job_result.traceback, list(job_result.job_log_entries.values_list("message", flat=True))),
|
|
413
418
|
)
|
|
414
419
|
self.repo.refresh_from_db()
|
|
415
420
|
MockGitRepo.assert_called_with(
|
|
@@ -433,10 +438,7 @@ class GitTest(TransactionTestCase):
|
|
|
433
438
|
self.assertEqual(
|
|
434
439
|
job_result.status,
|
|
435
440
|
JobResultStatusChoices.STATUS_SUCCESS,
|
|
436
|
-
(
|
|
437
|
-
job_result.result,
|
|
438
|
-
list(job_result.job_log_entries.filter(log_level="error").values_list("message", flat=True)),
|
|
439
|
-
),
|
|
441
|
+
(job_result.traceback, list(job_result.job_log_entries.values_list("message", flat=True))),
|
|
440
442
|
)
|
|
441
443
|
|
|
442
444
|
# Make sure explicit ConfigContext was successfully loaded from file
|
|
@@ -475,10 +477,7 @@ class GitTest(TransactionTestCase):
|
|
|
475
477
|
self.assertEqual(
|
|
476
478
|
job_result.status,
|
|
477
479
|
JobResultStatusChoices.STATUS_SUCCESS,
|
|
478
|
-
(
|
|
479
|
-
job_result.result,
|
|
480
|
-
list(job_result.job_log_entries.filter(log_level="error").values_list("message", flat=True)),
|
|
481
|
-
),
|
|
480
|
+
(job_result.traceback, list(job_result.job_log_entries.values_list("message", flat=True))),
|
|
482
481
|
)
|
|
483
482
|
|
|
484
483
|
# Verify that objects have been removed from the database
|
|
@@ -515,16 +514,16 @@ class GitTest(TransactionTestCase):
|
|
|
515
514
|
with self.settings(GIT_ROOT=tempdir):
|
|
516
515
|
|
|
517
516
|
def populate_repo(path, url):
|
|
518
|
-
os.makedirs(path)
|
|
519
|
-
os.makedirs(os.path.join(path, "config_contexts"))
|
|
520
|
-
os.makedirs(os.path.join(path, "config_contexts", "devices"))
|
|
521
|
-
os.makedirs(os.path.join(path, "config_context_schemas"))
|
|
522
|
-
os.makedirs(os.path.join(path, "export_templates", "nosuchapp", "device"))
|
|
523
|
-
os.makedirs(os.path.join(path, "export_templates", "dcim", "nosuchmodel"))
|
|
524
|
-
os.makedirs(os.path.join(path, "jobs"))
|
|
517
|
+
os.makedirs(path, exist_ok=True)
|
|
518
|
+
os.makedirs(os.path.join(path, "config_contexts"), exist_ok=True)
|
|
519
|
+
os.makedirs(os.path.join(path, "config_contexts", "devices"), exist_ok=True)
|
|
520
|
+
os.makedirs(os.path.join(path, "config_context_schemas"), exist_ok=True)
|
|
521
|
+
os.makedirs(os.path.join(path, "export_templates", "nosuchapp", "device"), exist_ok=True)
|
|
522
|
+
os.makedirs(os.path.join(path, "export_templates", "dcim", "nosuchmodel"), exist_ok=True)
|
|
523
|
+
os.makedirs(os.path.join(path, "jobs"), exist_ok=True)
|
|
525
524
|
# Incorrect directories
|
|
526
|
-
os.makedirs(os.path.join(path, "devices"))
|
|
527
|
-
os.makedirs(os.path.join(path, "dcim"))
|
|
525
|
+
os.makedirs(os.path.join(path, "devices"), exist_ok=True)
|
|
526
|
+
os.makedirs(os.path.join(path, "dcim"), exist_ok=True)
|
|
528
527
|
with open(os.path.join(path, "__init__.py"), "w") as fd:
|
|
529
528
|
pass
|
|
530
529
|
# Malformed JSON
|
|
@@ -563,6 +562,7 @@ class GitTest(TransactionTestCase):
|
|
|
563
562
|
|
|
564
563
|
# Run the Git operation and refresh the object from the DB
|
|
565
564
|
job_model = GitRepositorySync().job_model
|
|
565
|
+
self.assertIsNotNone(job_model)
|
|
566
566
|
job_result = run_job_for_testing(
|
|
567
567
|
job=job_model,
|
|
568
568
|
repository=self.repo.pk,
|
|
@@ -580,54 +580,65 @@ class GitTest(TransactionTestCase):
|
|
|
580
580
|
warning_logs = log_entries.filter(log_level=LogLevelChoices.LOG_WARNING)
|
|
581
581
|
failure_logs = log_entries.filter(log_level=LogLevelChoices.LOG_ERROR)
|
|
582
582
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
583
|
+
try:
|
|
584
|
+
warning_logs.get(
|
|
585
|
+
grouping="config contexts", message__contains='Found "devices" directory in the repository root'
|
|
586
|
+
)
|
|
587
|
+
warning_logs.get(
|
|
588
|
+
grouping="export templates", message__contains='Found "dcim" directory in the repository root'
|
|
589
|
+
)
|
|
590
|
+
warning_logs.get(
|
|
591
|
+
grouping="export templates",
|
|
592
|
+
message__contains="Skipping `dcim.nosuchmodel` as it isn't a known content type",
|
|
593
|
+
)
|
|
594
|
+
warning_logs.get(
|
|
595
|
+
grouping="export templates",
|
|
596
|
+
message__contains="Skipping `nosuchapp.device` as it isn't a known content type",
|
|
597
|
+
)
|
|
598
|
+
except JobLogEntry.DoesNotExist:
|
|
599
|
+
for log in log_entries:
|
|
600
|
+
print(log.message)
|
|
601
|
+
print(job_result.traceback)
|
|
602
|
+
raise
|
|
603
|
+
|
|
604
|
+
try:
|
|
605
|
+
failure_logs.get(
|
|
606
|
+
grouping="config context schemas",
|
|
607
|
+
message__contains="Error in loading config context schema data from `schema-1.yaml`",
|
|
608
|
+
)
|
|
609
|
+
failure_logs.get(
|
|
610
|
+
grouping="config context schemas",
|
|
611
|
+
message__contains="Error in loading config context schema data from `schema-2.yaml`: "
|
|
612
|
+
"data is missing the required `_metadata` key",
|
|
613
|
+
)
|
|
614
|
+
failure_logs.get(
|
|
615
|
+
grouping="config contexts",
|
|
616
|
+
message__contains="Error in loading config context data from `context.json`",
|
|
617
|
+
)
|
|
618
|
+
failure_logs.get(
|
|
619
|
+
grouping="config contexts",
|
|
620
|
+
message__contains="Error in loading config context data from `context2.json`: "
|
|
621
|
+
"data is missing the required `_metadata` key",
|
|
622
|
+
)
|
|
623
|
+
failure_logs.get(
|
|
624
|
+
grouping="config contexts",
|
|
625
|
+
message__contains="Error in loading config context data from `context3.json`: "
|
|
626
|
+
"data `_metadata` is missing the required `name` key",
|
|
627
|
+
)
|
|
628
|
+
failure_logs.get(
|
|
629
|
+
grouping="local config contexts",
|
|
630
|
+
message__contains="Error in loading local config context from `devices/nosuchdevice.json`: "
|
|
631
|
+
"record not found",
|
|
632
|
+
)
|
|
633
|
+
failure_logs.get(
|
|
634
|
+
grouping="jobs",
|
|
635
|
+
message__contains="Error in loading Jobs from Git repository: ",
|
|
636
|
+
)
|
|
637
|
+
except (AssertionError, JobLogEntry.DoesNotExist):
|
|
638
|
+
for log in log_entries:
|
|
639
|
+
print(log.message)
|
|
640
|
+
print(job_result.traceback)
|
|
641
|
+
raise
|
|
631
642
|
|
|
632
643
|
def test_delete_git_repository_cleanup(self, MockGitRepo):
|
|
633
644
|
"""
|
|
@@ -637,12 +648,12 @@ class GitTest(TransactionTestCase):
|
|
|
637
648
|
with self.settings(GIT_ROOT=tempdir):
|
|
638
649
|
|
|
639
650
|
def populate_repo(path, url):
|
|
640
|
-
os.makedirs(path)
|
|
641
|
-
os.makedirs(os.path.join(path, "config_contexts"))
|
|
642
|
-
os.makedirs(os.path.join(path, "config_contexts", "devices"))
|
|
643
|
-
os.makedirs(os.path.join(path, "config_context_schemas"))
|
|
644
|
-
os.makedirs(os.path.join(path, "export_templates", "dcim", "device"))
|
|
645
|
-
os.makedirs(os.path.join(path, "jobs"))
|
|
651
|
+
os.makedirs(path, exist_ok=True)
|
|
652
|
+
os.makedirs(os.path.join(path, "config_contexts"), exist_ok=True)
|
|
653
|
+
os.makedirs(os.path.join(path, "config_contexts", "devices"), exist_ok=True)
|
|
654
|
+
os.makedirs(os.path.join(path, "config_context_schemas"), exist_ok=True)
|
|
655
|
+
os.makedirs(os.path.join(path, "export_templates", "dcim", "device"), exist_ok=True)
|
|
656
|
+
os.makedirs(os.path.join(path, "jobs"), exist_ok=True)
|
|
646
657
|
with open(os.path.join(path, "__init__.py"), "w") as fd:
|
|
647
658
|
pass
|
|
648
659
|
with open(os.path.join(path, "config_contexts", "context.yaml"), "w") as fd:
|
|
@@ -701,10 +712,7 @@ class GitTest(TransactionTestCase):
|
|
|
701
712
|
self.assertEqual(
|
|
702
713
|
job_result.status,
|
|
703
714
|
JobResultStatusChoices.STATUS_SUCCESS,
|
|
704
|
-
(
|
|
705
|
-
job_result.result,
|
|
706
|
-
list(job_result.job_log_entries.filter(log_level="error").values_list("message", flat=True)),
|
|
707
|
-
),
|
|
715
|
+
(job_result.traceback, list(job_result.job_log_entries.values_list("message", flat=True))),
|
|
708
716
|
)
|
|
709
717
|
|
|
710
718
|
# Make sure ConfigContext was successfully loaded from file
|
|
@@ -735,7 +743,9 @@ class GitTest(TransactionTestCase):
|
|
|
735
743
|
self.assert_job_exists()
|
|
736
744
|
|
|
737
745
|
# Now delete the GitRepository
|
|
746
|
+
repo_pk = self.repo.pk
|
|
738
747
|
self.repo.delete()
|
|
748
|
+
self.repo = None
|
|
739
749
|
|
|
740
750
|
with self.subTest("Assert Deleted GitRepo do not create a never ending JobResult record"):
|
|
741
751
|
# Bug fix test for https://github.com/nautobot/nautobot/issues/5121
|
|
@@ -743,19 +753,19 @@ class GitTest(TransactionTestCase):
|
|
|
743
753
|
|
|
744
754
|
with self.assertRaises(ConfigContext.DoesNotExist):
|
|
745
755
|
ConfigContext.objects.get(
|
|
746
|
-
owner_object_id=
|
|
756
|
+
owner_object_id=repo_pk,
|
|
747
757
|
owner_content_type=ContentType.objects.get_for_model(GitRepository),
|
|
748
758
|
)
|
|
749
759
|
|
|
750
760
|
with self.assertRaises(ConfigContextSchema.DoesNotExist):
|
|
751
761
|
ConfigContextSchema.objects.get(
|
|
752
|
-
owner_object_id=
|
|
762
|
+
owner_object_id=repo_pk,
|
|
753
763
|
owner_content_type=ContentType.objects.get_for_model(GitRepository),
|
|
754
764
|
)
|
|
755
765
|
|
|
756
766
|
with self.assertRaises(ExportTemplate.DoesNotExist):
|
|
757
767
|
ExportTemplate.objects.get(
|
|
758
|
-
owner_object_id=
|
|
768
|
+
owner_object_id=repo_pk,
|
|
759
769
|
owner_content_type=ContentType.objects.get_for_model(GitRepository),
|
|
760
770
|
)
|
|
761
771
|
|
|
@@ -774,6 +784,7 @@ class GitTest(TransactionTestCase):
|
|
|
774
784
|
return mock.DEFAULT
|
|
775
785
|
|
|
776
786
|
MockGitRepo.side_effect = create_empty_repo
|
|
787
|
+
MockGitRepo.return_value.checkout.return_value = (self.COMMIT_HEXSHA, False)
|
|
777
788
|
|
|
778
789
|
self.mock_request.id = uuid.uuid4()
|
|
779
790
|
|
|
@@ -787,13 +798,9 @@ class GitTest(TransactionTestCase):
|
|
|
787
798
|
self.assertEqual(
|
|
788
799
|
job_result.status,
|
|
789
800
|
JobResultStatusChoices.STATUS_SUCCESS,
|
|
790
|
-
(
|
|
791
|
-
job_result.result,
|
|
792
|
-
list(job_result.job_log_entries.filter(log_level="error").values_list("message", flat=True)),
|
|
793
|
-
),
|
|
801
|
+
(job_result.traceback, list(job_result.job_log_entries.values_list("message", flat=True))),
|
|
794
802
|
)
|
|
795
803
|
|
|
796
|
-
MockGitRepo.return_value.checkout.assert_not_called()
|
|
797
804
|
MockGitRepo.assert_called_with(
|
|
798
805
|
os.path.join(tempdir, self.repo.slug),
|
|
799
806
|
self.repo.remote_url,
|
|
@@ -3,26 +3,32 @@ from django.test import TestCase
|
|
|
3
3
|
from netaddr import IPAddress, IPNetwork
|
|
4
4
|
|
|
5
5
|
from nautobot.dcim.models import Device
|
|
6
|
-
from nautobot.extras.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
from nautobot.extras.jobs import (
|
|
7
|
+
BooleanVar,
|
|
8
|
+
ChoiceVar,
|
|
9
|
+
FileVar,
|
|
10
|
+
IntegerVar,
|
|
11
|
+
IPAddressVar,
|
|
12
|
+
IPAddressWithMaskVar,
|
|
13
|
+
IPNetworkVar,
|
|
14
|
+
Job,
|
|
15
|
+
JSONVar,
|
|
16
|
+
MultiChoiceVar,
|
|
17
|
+
MultiObjectVar,
|
|
18
|
+
ObjectVar,
|
|
19
|
+
StringVar,
|
|
20
|
+
TextVar,
|
|
21
21
|
)
|
|
22
|
+
from nautobot.extras.models import Role
|
|
23
|
+
|
|
24
|
+
CHOICES = (("ff0000", "Red"), ("00ff00", "Green"), ("0000ff", "Blue"))
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class JobVariablesTest(TestCase):
|
|
25
28
|
def test_stringvar(self):
|
|
29
|
+
class StringVarJob(Job):
|
|
30
|
+
var1 = StringVar(min_length=3, max_length=3, regex=r"[a-z]+")
|
|
31
|
+
|
|
26
32
|
# Validate min_length enforcement
|
|
27
33
|
data = {"var1": "xx"}
|
|
28
34
|
form = StringVarJob().as_form(data)
|
|
@@ -48,6 +54,9 @@ class JobVariablesTest(TestCase):
|
|
|
48
54
|
self.assertEqual(form.cleaned_data["var1"], data["var1"])
|
|
49
55
|
|
|
50
56
|
def test_textvar(self):
|
|
57
|
+
class TextVarJob(Job):
|
|
58
|
+
var1 = TextVar()
|
|
59
|
+
|
|
51
60
|
# Validate valid data
|
|
52
61
|
data = {"var1": "This is a test string"}
|
|
53
62
|
form = TextVarJob().as_form(data)
|
|
@@ -55,6 +64,9 @@ class JobVariablesTest(TestCase):
|
|
|
55
64
|
self.assertEqual(form.cleaned_data["var1"], data["var1"])
|
|
56
65
|
|
|
57
66
|
def test_integervar(self):
|
|
67
|
+
class IntegerVarJob(Job):
|
|
68
|
+
var1 = IntegerVar(min_value=5, max_value=10)
|
|
69
|
+
|
|
58
70
|
# Validate min_value enforcement
|
|
59
71
|
data = {"var1": 4}
|
|
60
72
|
form = IntegerVarJob().as_form(data)
|
|
@@ -74,6 +86,9 @@ class JobVariablesTest(TestCase):
|
|
|
74
86
|
self.assertEqual(form.cleaned_data["var1"], data["var1"])
|
|
75
87
|
|
|
76
88
|
def test_booleanvar(self):
|
|
89
|
+
class BooleanVarJob(Job):
|
|
90
|
+
var1 = BooleanVar()
|
|
91
|
+
|
|
77
92
|
# Validate True
|
|
78
93
|
data = {"var1": True}
|
|
79
94
|
form = BooleanVarJob().as_form(data)
|
|
@@ -87,6 +102,9 @@ class JobVariablesTest(TestCase):
|
|
|
87
102
|
self.assertEqual(form.cleaned_data["var1"], False)
|
|
88
103
|
|
|
89
104
|
def test_choicevar(self):
|
|
105
|
+
class ChoiceVarJob(Job):
|
|
106
|
+
var1 = ChoiceVar(choices=CHOICES)
|
|
107
|
+
|
|
90
108
|
# Validate valid choice
|
|
91
109
|
data = {"var1": "ff0000"}
|
|
92
110
|
form = ChoiceVarJob().as_form(data)
|
|
@@ -100,6 +118,9 @@ class JobVariablesTest(TestCase):
|
|
|
100
118
|
self.assertIn("var1", form.errors)
|
|
101
119
|
|
|
102
120
|
def test_multichoicevar(self):
|
|
121
|
+
class MultiChoiceVarJob(Job):
|
|
122
|
+
var1 = MultiChoiceVar(choices=CHOICES)
|
|
123
|
+
|
|
103
124
|
# Validate single choice
|
|
104
125
|
data = {"var1": ["ff0000"]}
|
|
105
126
|
form = MultiChoiceVarJob().as_form(data)
|
|
@@ -119,6 +140,9 @@ class JobVariablesTest(TestCase):
|
|
|
119
140
|
self.assertIn("var1", form.errors)
|
|
120
141
|
|
|
121
142
|
def test_objectvar(self):
|
|
143
|
+
class ObjectVarJob(Job):
|
|
144
|
+
var1 = ObjectVar(model=Role)
|
|
145
|
+
|
|
122
146
|
# Validate valid data
|
|
123
147
|
data = {"var1": Role.objects.get_for_model(Device).first().pk}
|
|
124
148
|
form = ObjectVarJob().as_form(data)
|
|
@@ -126,6 +150,9 @@ class JobVariablesTest(TestCase):
|
|
|
126
150
|
self.assertEqual(form.cleaned_data["var1"].pk, data["var1"])
|
|
127
151
|
|
|
128
152
|
def test_multiobjectvar(self):
|
|
153
|
+
class MultiObjectVarJob(Job):
|
|
154
|
+
var1 = MultiObjectVar(model=Role)
|
|
155
|
+
|
|
129
156
|
# Validate valid data
|
|
130
157
|
data = {"var1": [role.pk for role in Role.objects.all()[:3]]}
|
|
131
158
|
form = MultiObjectVarJob().as_form(data)
|
|
@@ -135,6 +162,9 @@ class JobVariablesTest(TestCase):
|
|
|
135
162
|
self.assertEqual(form.cleaned_data["var1"][2].pk, data["var1"][2])
|
|
136
163
|
|
|
137
164
|
def test_filevar(self):
|
|
165
|
+
class FileVarJob(Job):
|
|
166
|
+
var1 = FileVar()
|
|
167
|
+
|
|
138
168
|
# Test file
|
|
139
169
|
testfile = SimpleUploadedFile(name="test_file.txt", content=b"This is an test file for testing")
|
|
140
170
|
|
|
@@ -145,6 +175,9 @@ class JobVariablesTest(TestCase):
|
|
|
145
175
|
self.assertEqual(form.cleaned_data["var1"], testfile)
|
|
146
176
|
|
|
147
177
|
def test_ipaddressvar(self):
|
|
178
|
+
class IPAddressVarJob(Job):
|
|
179
|
+
var1 = IPAddressVar()
|
|
180
|
+
|
|
148
181
|
# Validate IP network enforcement
|
|
149
182
|
data = {"var1": "1.2.3"}
|
|
150
183
|
form = IPAddressVarJob().as_form(data)
|
|
@@ -164,6 +197,9 @@ class JobVariablesTest(TestCase):
|
|
|
164
197
|
self.assertEqual(form.cleaned_data["var1"], IPAddress(data["var1"]))
|
|
165
198
|
|
|
166
199
|
def test_ipaddresswithmaskvar(self):
|
|
200
|
+
class IPAddressWithMaskVarJob(Job):
|
|
201
|
+
var1 = IPAddressWithMaskVar()
|
|
202
|
+
|
|
167
203
|
# Validate IP network enforcement
|
|
168
204
|
data = {"var1": "1.2.3"}
|
|
169
205
|
form = IPAddressWithMaskVarJob().as_form(data)
|
|
@@ -183,6 +219,9 @@ class JobVariablesTest(TestCase):
|
|
|
183
219
|
self.assertEqual(form.cleaned_data["var1"], IPNetwork(data["var1"]))
|
|
184
220
|
|
|
185
221
|
def test_ipnetworkvar(self):
|
|
222
|
+
class IPNetworkVarJob(Job):
|
|
223
|
+
var1 = IPNetworkVar()
|
|
224
|
+
|
|
186
225
|
# Validate IP network enforcement
|
|
187
226
|
data = {"var1": "1.2.3"}
|
|
188
227
|
form = IPNetworkVarJob().as_form(data)
|
|
@@ -202,6 +241,9 @@ class JobVariablesTest(TestCase):
|
|
|
202
241
|
self.assertEqual(form.cleaned_data["var1"], IPNetwork(data["var1"]))
|
|
203
242
|
|
|
204
243
|
def test_jsonvar(self):
|
|
244
|
+
class JSONVarJob(Job):
|
|
245
|
+
var1 = JSONVar()
|
|
246
|
+
|
|
205
247
|
# Valid JSON value as dictionary
|
|
206
248
|
data = {"var1": {"key1": "value1"}}
|
|
207
249
|
form = JSONVarJob().as_form(data)
|