nautobot 2.2.4__py3-none-any.whl → 2.2.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/api.py +2 -0
- nautobot/apps/models.py +2 -0
- nautobot/circuits/forms.py +15 -0
- nautobot/circuits/navigation.py +9 -1
- nautobot/circuits/views.py +2 -0
- nautobot/core/api/fields.py +13 -0
- nautobot/core/api/serializers.py +7 -1
- nautobot/core/filters.py +11 -0
- nautobot/core/management/commands/generate_test_data.py +128 -158
- nautobot/core/models/fields.py +15 -0
- nautobot/core/settings.yaml +3 -3
- nautobot/core/testing/filters.py +24 -1
- nautobot/core/testing/views.py +13 -1
- nautobot/core/tests/test_utils.py +48 -1
- nautobot/core/utils/git.py +121 -49
- nautobot/core/utils/module_loading.py +10 -2
- nautobot/core/views/utils.py +18 -1
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filters/__init__.py +1 -1
- nautobot/dcim/forms.py +23 -4
- nautobot/dcim/tables/devicetypes.py +15 -4
- nautobot/dcim/tests/test_models.py +2 -0
- nautobot/dcim/tests/test_views.py +84 -0
- nautobot/dcim/views.py +3 -0
- nautobot/extras/api/views.py +2 -2
- nautobot/extras/context_managers.py +3 -0
- nautobot/extras/datasources/git.py +133 -135
- nautobot/extras/datasources/utils.py +3 -0
- nautobot/extras/filters/__init__.py +16 -1
- nautobot/extras/forms/forms.py +49 -3
- nautobot/extras/forms/mixins.py +0 -6
- nautobot/extras/jobs.py +9 -1
- nautobot/extras/migrations/0107_laxurlfield.py +28 -0
- nautobot/extras/migrations/0108_jobbutton_enabled.py +17 -0
- nautobot/extras/models/datasources.py +6 -4
- nautobot/extras/models/jobs.py +30 -0
- nautobot/extras/models/models.py +2 -4
- nautobot/extras/signals.py +6 -1
- nautobot/extras/tables.py +3 -0
- nautobot/extras/templates/extras/jobbutton_retrieve.html +6 -2
- nautobot/extras/templatetags/job_buttons.py +2 -2
- nautobot/extras/tests/git_data/01-valid-files/__init__.py +0 -0
- nautobot/extras/tests/git_data/01-valid-files/config_context_schemas/schema-1.yaml +18 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/context.yaml +12 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/devices/test-device.json +3 -0
- nautobot/extras/tests/git_data/01-valid-files/config_contexts/locations/Test Location.json +7 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template.j2 +3 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/dcim/device/template2.html +4 -0
- nautobot/extras/tests/git_data/01-valid-files/export_templates/ipam/vlan/template.j2 +3 -0
- nautobot/extras/tests/git_data/01-valid-files/jobs/__init__.py +5 -0
- nautobot/extras/tests/git_data/01-valid-files/jobs/my_job.py +16 -0
- nautobot/extras/tests/git_data/02-invalid-files/__init__.py +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema1.json +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_context_schemas/badschema2.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext1.json +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext2.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/badcontext3.json +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/config_contexts/devices/nosuchdevice.json +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/dcim/template.j2 +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/devices/template.j2 +0 -0
- nautobot/extras/tests/git_data/02-invalid-files/export_templates/dcim/nosuchmodel/template.j2 +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/export_templates/nosuchapp/device/template.j2 +3 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/__init__.py +2 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/importerror.py +1 -0
- nautobot/extras/tests/git_data/02-invalid-files/jobs/syntaxerror.py +1 -0
- nautobot/extras/tests/git_helper.py +76 -0
- nautobot/extras/tests/test_api.py +52 -13
- nautobot/extras/tests/test_context_managers.py +33 -1
- nautobot/extras/tests/test_datasources.py +94 -276
- nautobot/extras/tests/test_filters.py +69 -0
- nautobot/extras/tests/test_forms.py +0 -3
- nautobot/extras/tests/test_models.py +8 -3
- nautobot/extras/tests/test_views.py +69 -11
- nautobot/extras/views.py +12 -10
- nautobot/ipam/filters.py +9 -1
- nautobot/ipam/forms.py +26 -0
- nautobot/ipam/tables.py +1 -1
- nautobot/ipam/tests/test_filters.py +15 -0
- nautobot/ipam/tests/test_views.py +9 -2
- nautobot/ipam/views.py +11 -0
- nautobot/project-static/docs/404.html +84 -9
- nautobot/project-static/docs/apps/index.html +97 -11
- nautobot/project-static/docs/apps/nautobot-apps.html +97 -11
- nautobot/project-static/docs/assets/app-icons/icon-CapacityMetrics.svg +1 -0
- nautobot/project-static/docs/assets/app-icons/icon-CircuitMaintenance.png +0 -0
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js → bundle.ad660dcc.min.js} +6 -6
- nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js.map → bundle.ad660dcc.min.js.map} +3 -3
- nautobot/project-static/docs/assets/javascripts/glightbox.min.js +1 -0
- nautobot/project-static/docs/assets/stylesheets/glightbox.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css → main.6543a935.min.css} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css.map → main.6543a935.min.css.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +157 -13
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +97 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +144 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +97 -11
- nautobot/project-static/docs/development/apps/api/configuration-view.html +97 -11
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/graphql.html +97 -11
- nautobot/project-static/docs/development/apps/api/models/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +98 -12
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +97 -11
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +97 -11
- nautobot/project-static/docs/development/apps/api/prometheus.html +97 -11
- nautobot/project-static/docs/development/apps/api/setup.html +97 -11
- nautobot/project-static/docs/development/apps/api/testing.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +97 -11
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/base-template.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/index.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/notes.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +97 -11
- nautobot/project-static/docs/development/apps/api/views/urls.html +97 -11
- nautobot/project-static/docs/development/apps/index.html +97 -11
- nautobot/project-static/docs/development/apps/migration/code-updates.html +98 -12
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +97 -11
- nautobot/project-static/docs/development/apps/migration/from-v1.html +99 -13
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +97 -11
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +97 -11
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +97 -11
- nautobot/project-static/docs/development/core/application-registry.html +98 -12
- nautobot/project-static/docs/development/core/best-practices.html +97 -11
- nautobot/project-static/docs/development/core/bootstrap-ui.html +97 -11
- nautobot/project-static/docs/development/core/caching.html +97 -11
- nautobot/project-static/docs/development/core/controllers.html +97 -11
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +97 -11
- nautobot/project-static/docs/development/core/generic-views.html +97 -11
- nautobot/project-static/docs/development/core/getting-started.html +99 -13
- nautobot/project-static/docs/development/core/homepage.html +97 -11
- nautobot/project-static/docs/development/core/index.html +97 -11
- nautobot/project-static/docs/development/core/model-checklist.html +97 -11
- nautobot/project-static/docs/development/core/model-features.html +97 -11
- nautobot/project-static/docs/development/core/natural-keys.html +97 -11
- nautobot/project-static/docs/development/core/navigation-menu.html +97 -11
- nautobot/project-static/docs/development/core/release-checklist.html +97 -11
- nautobot/project-static/docs/development/core/role-internals.html +97 -11
- nautobot/project-static/docs/development/core/settings.html +97 -11
- nautobot/project-static/docs/development/core/style-guide.html +97 -11
- nautobot/project-static/docs/development/core/templates.html +97 -11
- nautobot/project-static/docs/development/core/testing.html +98 -12
- nautobot/project-static/docs/development/core/user-preferences.html +97 -11
- nautobot/project-static/docs/development/index.html +97 -11
- nautobot/project-static/docs/development/jobs/index.html +97 -11
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +97 -11
- nautobot/project-static/docs/index.html +13 -8362
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +8229 -0
- nautobot/project-static/docs/overview/design_philosophy.html +8158 -0
- nautobot/project-static/docs/overview/index.html +8230 -0
- nautobot/project-static/docs/release-notes/index.html +97 -11
- nautobot/project-static/docs/release-notes/version-1.0.html +98 -12
- nautobot/project-static/docs/release-notes/version-1.1.html +97 -11
- nautobot/project-static/docs/release-notes/version-1.2.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.3.html +98 -12
- nautobot/project-static/docs/release-notes/version-1.4.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.5.html +99 -13
- nautobot/project-static/docs/release-notes/version-1.6.html +626 -160
- nautobot/project-static/docs/release-notes/version-2.0.html +100 -14
- nautobot/project-static/docs/release-notes/version-2.1.html +97 -11
- nautobot/project-static/docs/release-notes/version-2.2.html +546 -107
- nautobot/project-static/docs/requirements.txt +3 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +268 -258
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +99 -13
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +98 -12
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +100 -14
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +97 -11
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +98 -12
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +97 -11
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/index.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +101 -15
- nautobot/project-static/docs/user-guide/administration/installation/services.html +98 -12
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +97 -11
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +97 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +97 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +97 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +97 -11
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +98 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +97 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +97 -30
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +100 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +99 -13
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +98 -12
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +97 -11
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +100 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +98 -12
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +98 -12
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +97 -11
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +97 -11
- nautobot/project-static/docs/user-guide/index.html +100 -14
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +98 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +98 -12
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +102 -15
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +99 -13
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +98 -44
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +97 -11
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +97 -11
- nautobot/project-static/js/forms.js +2 -1
- nautobot/tenancy/forms.py +9 -0
- nautobot/tenancy/views.py +1 -0
- nautobot/virtualization/forms.py +18 -6
- nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
- nautobot/virtualization/views.py +2 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/METADATA +1 -1
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/RECORD +364 -331
- nautobot/extras/tests/test_git.py +0 -23
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/NOTICE +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/WHEEL +0 -0
- {nautobot-2.2.4.dist-info → nautobot-2.2.6.dist-info}/entry_points.txt +0 -0
|
@@ -30,6 +30,7 @@ from nautobot.extras.constants import HTTP_CONTENT_TYPE_JSON
|
|
|
30
30
|
from nautobot.extras.filters import (
|
|
31
31
|
ComputedFieldFilterSet,
|
|
32
32
|
ConfigContextFilterSet,
|
|
33
|
+
ContactAssociationFilterSet,
|
|
33
34
|
ContactFilterSet,
|
|
34
35
|
ContentTypeFilterSet,
|
|
35
36
|
CustomFieldChoiceFilterSet,
|
|
@@ -555,6 +556,74 @@ class ContactFilterSetTestCase(ContactAndTeamFilterSetTestCaseMixin, FilterTestC
|
|
|
555
556
|
)
|
|
556
557
|
|
|
557
558
|
|
|
559
|
+
class ContactAssociationFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
560
|
+
queryset = ContactAssociation.objects.all()
|
|
561
|
+
filterset = ContactAssociationFilterSet
|
|
562
|
+
|
|
563
|
+
generic_filter_tests = (
|
|
564
|
+
["status", "status__id"],
|
|
565
|
+
["status", "status__name"],
|
|
566
|
+
["contact", "contact__id"],
|
|
567
|
+
["contact", "contact__name"],
|
|
568
|
+
["team", "team__id"],
|
|
569
|
+
["team", "team__name"],
|
|
570
|
+
["role", "role__id"],
|
|
571
|
+
["role", "role__name"],
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
@classmethod
|
|
575
|
+
def setUpTestData(cls):
|
|
576
|
+
roles = Role.objects.get_for_model(ContactAssociation)
|
|
577
|
+
statuses = Status.objects.get_for_model(ContactAssociation)
|
|
578
|
+
ip_addresses = IPAddress.objects.all()
|
|
579
|
+
locations = Location.objects.all()
|
|
580
|
+
|
|
581
|
+
cls.location_ct = ContentType.objects.get_for_model(Location)
|
|
582
|
+
ipaddress_ct = ContentType.objects.get_for_model(IPAddress)
|
|
583
|
+
|
|
584
|
+
ContactAssociation.objects.create(
|
|
585
|
+
contact=Contact.objects.first(),
|
|
586
|
+
associated_object_type=ipaddress_ct,
|
|
587
|
+
associated_object_id=ip_addresses[0].pk,
|
|
588
|
+
role=roles[2],
|
|
589
|
+
status=statuses[1],
|
|
590
|
+
)
|
|
591
|
+
ContactAssociation.objects.create(
|
|
592
|
+
contact=Contact.objects.last(),
|
|
593
|
+
associated_object_type=ipaddress_ct,
|
|
594
|
+
associated_object_id=ip_addresses[1].pk,
|
|
595
|
+
role=roles[1],
|
|
596
|
+
status=statuses[2],
|
|
597
|
+
)
|
|
598
|
+
ContactAssociation.objects.create(
|
|
599
|
+
team=Team.objects.first(),
|
|
600
|
+
associated_object_type=cls.location_ct,
|
|
601
|
+
associated_object_id=locations[0].pk,
|
|
602
|
+
role=roles[3],
|
|
603
|
+
status=statuses[0],
|
|
604
|
+
)
|
|
605
|
+
ContactAssociation.objects.create(
|
|
606
|
+
team=Team.objects.last(),
|
|
607
|
+
associated_object_type=cls.location_ct,
|
|
608
|
+
associated_object_id=locations[1].pk,
|
|
609
|
+
role=roles[0],
|
|
610
|
+
status=statuses[1],
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
def test_associated_object_type(self):
|
|
614
|
+
params = {"associated_object_type": "dcim.location"}
|
|
615
|
+
self.assertEqual(
|
|
616
|
+
self.filterset(params, self.queryset).qs.count(),
|
|
617
|
+
ContactAssociation.objects.filter(associated_object_type=self.location_ct).count(),
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
params = {"associated_object_type": self.location_ct.pk}
|
|
621
|
+
self.assertEqual(
|
|
622
|
+
self.filterset(params, self.queryset).qs.count(),
|
|
623
|
+
ContactAssociation.objects.filter(associated_object_type=self.location_ct).count(),
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
|
|
558
627
|
class CustomFieldChoiceFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
559
628
|
queryset = CustomFieldChoice.objects.all()
|
|
560
629
|
filterset = CustomFieldChoiceFilterSet
|
|
@@ -14,7 +14,6 @@ from nautobot.extras.forms import (
|
|
|
14
14
|
ConfigContextFilterForm,
|
|
15
15
|
ConfigContextForm,
|
|
16
16
|
CustomFieldModelBulkEditFormMixin,
|
|
17
|
-
CustomFieldModelFilterFormMixin,
|
|
18
17
|
CustomFieldModelFormMixin,
|
|
19
18
|
JobButtonForm,
|
|
20
19
|
JobEditForm,
|
|
@@ -1089,7 +1088,6 @@ class DeprecatedAliasesTestCase(TestCase):
|
|
|
1089
1088
|
AddRemoveTagsForm,
|
|
1090
1089
|
CustomFieldBulkCreateForm,
|
|
1091
1090
|
CustomFieldBulkEditForm,
|
|
1092
|
-
CustomFieldFilterForm,
|
|
1093
1091
|
CustomFieldModelForm,
|
|
1094
1092
|
RelationshipModelForm,
|
|
1095
1093
|
StatusBulkEditFormMixin,
|
|
@@ -1100,7 +1098,6 @@ class DeprecatedAliasesTestCase(TestCase):
|
|
|
1100
1098
|
(AddRemoveTagsForm, TagsBulkEditFormMixin),
|
|
1101
1099
|
(CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
|
|
1102
1100
|
(CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
|
|
1103
|
-
(CustomFieldFilterForm, CustomFieldModelFilterFormMixin),
|
|
1104
1101
|
(CustomFieldModelForm, CustomFieldModelFormMixin),
|
|
1105
1102
|
(RelationshipModelForm, RelationshipModelFormMixin),
|
|
1106
1103
|
(StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
|
|
@@ -254,7 +254,7 @@ class ConfigContextTest(ModelTestCases.BaseModelTestCase):
|
|
|
254
254
|
slug="test_git_repo",
|
|
255
255
|
remote_url="http://localhost/git.git",
|
|
256
256
|
)
|
|
257
|
-
repo.
|
|
257
|
+
repo.validated_save()
|
|
258
258
|
|
|
259
259
|
with self.assertRaises(ValidationError):
|
|
260
260
|
nonduplicate_context = ConfigContext(name="context 1", weight=300, data={"a": "22"}, owner=repo)
|
|
@@ -864,7 +864,7 @@ class ExportTemplateTest(ModelTestCases.BaseModelTestCase):
|
|
|
864
864
|
slug="test_git_repo",
|
|
865
865
|
remote_url="http://localhost/git.git",
|
|
866
866
|
)
|
|
867
|
-
repo.
|
|
867
|
+
repo.validated_save()
|
|
868
868
|
|
|
869
869
|
with self.assertRaises(ValidationError):
|
|
870
870
|
nonduplicate_template = ExportTemplate(
|
|
@@ -888,7 +888,7 @@ class ExternalIntegrationTest(ModelTestCases.BaseModelTestCase):
|
|
|
888
888
|
)
|
|
889
889
|
ei.validated_save()
|
|
890
890
|
|
|
891
|
-
ei.remote_url = "http://
|
|
891
|
+
ei.remote_url = "http://some-local-host"
|
|
892
892
|
ei.validated_save()
|
|
893
893
|
|
|
894
894
|
def test_timeout_validation(self):
|
|
@@ -1045,6 +1045,11 @@ class GitRepositoryTest(ModelTestCases.BaseModelTestCase):
|
|
|
1045
1045
|
repo.validated_save()
|
|
1046
1046
|
self.assertIn("Please choose a different slug", str(handler.exception))
|
|
1047
1047
|
|
|
1048
|
+
def test_remote_url_hostname(self):
|
|
1049
|
+
"""Confirm that a bare hostname (no domain name) can be used for a remote URL."""
|
|
1050
|
+
self.repo.remote_url = "http://some-private-host/example.git"
|
|
1051
|
+
self.repo.validated_save()
|
|
1052
|
+
|
|
1048
1053
|
|
|
1049
1054
|
class JobModelTest(ModelTestCases.BaseModelTestCase):
|
|
1050
1055
|
"""
|
|
@@ -17,6 +17,7 @@ from nautobot.core.choices import ColorChoices
|
|
|
17
17
|
from nautobot.core.models.fields import slugify_dashes_to_underscores
|
|
18
18
|
from nautobot.core.testing import extract_form_failures, extract_page_body, TestCase, ViewTestCases
|
|
19
19
|
from nautobot.core.testing.utils import disable_warnings, post_data
|
|
20
|
+
from nautobot.core.utils.permissions import get_permission_for_model
|
|
20
21
|
from nautobot.dcim.models import (
|
|
21
22
|
ConsolePort,
|
|
22
23
|
Controller,
|
|
@@ -777,9 +778,11 @@ class DynamicGroupTestCase(
|
|
|
777
778
|
content_type = ContentType.objects.get_for_model(Device)
|
|
778
779
|
|
|
779
780
|
# DynamicGroup objects to test.
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
781
|
+
cls.dynamic_groups = [
|
|
782
|
+
DynamicGroup.objects.create(name="DG 1", content_type=content_type),
|
|
783
|
+
DynamicGroup.objects.create(name="DG 2", content_type=content_type),
|
|
784
|
+
DynamicGroup.objects.create(name="DG 3", content_type=content_type),
|
|
785
|
+
]
|
|
783
786
|
|
|
784
787
|
cls.form_data = {
|
|
785
788
|
"name": "new_dynamic_group",
|
|
@@ -792,6 +795,38 @@ class DynamicGroupTestCase(
|
|
|
792
795
|
"dynamic_group_memberships-MAX_NUM_FORMS": "1000",
|
|
793
796
|
}
|
|
794
797
|
|
|
798
|
+
def test_get_object_with_permission(self):
|
|
799
|
+
instance = self._get_queryset().first()
|
|
800
|
+
# Add view permissions for the group's members:
|
|
801
|
+
self.add_permissions(get_permission_for_model(instance.content_type.model_class(), "view"))
|
|
802
|
+
|
|
803
|
+
response = super().test_get_object_with_permission()
|
|
804
|
+
|
|
805
|
+
response_body = extract_page_body(response.content.decode(response.charset))
|
|
806
|
+
# Check that the "members" table in the detail view includes all appropriate member objects
|
|
807
|
+
for member in instance.members:
|
|
808
|
+
self.assertIn(str(member.pk), response_body)
|
|
809
|
+
|
|
810
|
+
def test_get_object_with_constrained_permission(self):
|
|
811
|
+
instance = self._get_queryset().first()
|
|
812
|
+
# Add view permission for one of the group's members but not the others:
|
|
813
|
+
member1, member2 = instance.members[:2]
|
|
814
|
+
obj_perm = ObjectPermission(
|
|
815
|
+
name="Members permission",
|
|
816
|
+
constraints={"pk": member1.pk},
|
|
817
|
+
actions=["view"],
|
|
818
|
+
)
|
|
819
|
+
obj_perm.save()
|
|
820
|
+
obj_perm.users.add(self.user)
|
|
821
|
+
obj_perm.object_types.add(instance.content_type)
|
|
822
|
+
|
|
823
|
+
response = super().test_get_object_with_constrained_permission()
|
|
824
|
+
|
|
825
|
+
response_body = extract_page_body(response.content.decode(response.charset))
|
|
826
|
+
# Check that the "members" table in the detail view includes all permitted member objects
|
|
827
|
+
self.assertIn(str(member1.pk), response_body)
|
|
828
|
+
self.assertNotIn(str(member2.pk), response_body)
|
|
829
|
+
|
|
795
830
|
def test_get_object_dynamic_groups_anonymous(self):
|
|
796
831
|
url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.first().pk})
|
|
797
832
|
self.client.logout()
|
|
@@ -815,7 +850,6 @@ class DynamicGroupTestCase(
|
|
|
815
850
|
self.assertIn("DG 3", response_body, msg=response_body)
|
|
816
851
|
|
|
817
852
|
def test_get_object_dynamic_groups_with_constrained_permission(self):
|
|
818
|
-
self.add_permissions("extras.view_dynamicgroup")
|
|
819
853
|
obj_perm = ObjectPermission(
|
|
820
854
|
name="View a device",
|
|
821
855
|
constraints={"pk": Device.objects.first().pk},
|
|
@@ -824,12 +858,22 @@ class DynamicGroupTestCase(
|
|
|
824
858
|
obj_perm.save()
|
|
825
859
|
obj_perm.users.add(self.user)
|
|
826
860
|
obj_perm.object_types.add(ContentType.objects.get_for_model(Device))
|
|
861
|
+
obj_perm_2 = ObjectPermission(
|
|
862
|
+
name="View a Dynamic Group",
|
|
863
|
+
constraints={"pk": self.dynamic_groups[0].pk},
|
|
864
|
+
actions=["view"],
|
|
865
|
+
)
|
|
866
|
+
obj_perm_2.save()
|
|
867
|
+
obj_perm_2.users.add(self.user)
|
|
868
|
+
obj_perm_2.object_types.add(ContentType.objects.get_for_model(DynamicGroup))
|
|
827
869
|
|
|
828
870
|
url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.first().pk})
|
|
829
871
|
response = self.client.get(url)
|
|
830
872
|
self.assertHttpStatus(response, 200)
|
|
831
873
|
response_body = response.content.decode(response.charset)
|
|
832
874
|
self.assertIn("DG 1", response_body, msg=response_body)
|
|
875
|
+
self.assertNotIn("DG 2", response_body, msg=response_body)
|
|
876
|
+
self.assertNotIn("DG 3", response_body, msg=response_body)
|
|
833
877
|
|
|
834
878
|
url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.last().pk})
|
|
835
879
|
response = self.client.get(url)
|
|
@@ -952,7 +996,7 @@ class GitRepositoryTestCase(
|
|
|
952
996
|
# Create four GitRepository records
|
|
953
997
|
repos = (
|
|
954
998
|
GitRepository(name="Repo 1", slug="repo_1", remote_url="https://example.com/repo1.git"),
|
|
955
|
-
GitRepository(name="Repo 2", slug="repo_2", remote_url="https://
|
|
999
|
+
GitRepository(name="Repo 2", slug="repo_2", remote_url="https://some-local-host/repo2.git"),
|
|
956
1000
|
GitRepository(name="Repo 3", slug="repo_3", remote_url="https://example.com/repo3.git"),
|
|
957
1001
|
GitRepository(name="Repo 4", remote_url="https://example.com/repo4.git", secrets_group=secrets_groups[0]),
|
|
958
1002
|
)
|
|
@@ -962,7 +1006,7 @@ class GitRepositoryTestCase(
|
|
|
962
1006
|
cls.form_data = {
|
|
963
1007
|
"name": "A new Git repository",
|
|
964
1008
|
"slug": "a_new_git_repository",
|
|
965
|
-
"remote_url": "http://
|
|
1009
|
+
"remote_url": "http://another-local-host/a_new_git_repository.git",
|
|
966
1010
|
"branch": "develop",
|
|
967
1011
|
"_token": "1234567890abcdef1234567890abcdef",
|
|
968
1012
|
"secrets_group": secrets_groups[1].pk,
|
|
@@ -2285,23 +2329,30 @@ class JobButtonTestCase(
|
|
|
2285
2329
|
|
|
2286
2330
|
@classmethod
|
|
2287
2331
|
def setUpTestData(cls):
|
|
2332
|
+
jbr_simple = Job.objects.get(job_class_name="TestJobButtonReceiverSimple")
|
|
2333
|
+
jbr_simple.enabled = True
|
|
2334
|
+
jbr_simple.save()
|
|
2335
|
+
jbr_complex = Job.objects.get(job_class_name="TestJobButtonReceiverComplex")
|
|
2336
|
+
jbr_complex.enabled = True
|
|
2337
|
+
jbr_complex.save()
|
|
2338
|
+
|
|
2288
2339
|
job_buttons = (
|
|
2289
2340
|
JobButton.objects.create(
|
|
2290
2341
|
name="JobButton1",
|
|
2291
2342
|
text="JobButton1",
|
|
2292
|
-
job=
|
|
2343
|
+
job=jbr_simple,
|
|
2293
2344
|
confirmation=True,
|
|
2294
2345
|
),
|
|
2295
2346
|
JobButton.objects.create(
|
|
2296
2347
|
name="JobButton2",
|
|
2297
2348
|
text="JobButton2",
|
|
2298
|
-
job=
|
|
2349
|
+
job=jbr_simple,
|
|
2299
2350
|
confirmation=False,
|
|
2300
2351
|
),
|
|
2301
2352
|
JobButton.objects.create(
|
|
2302
2353
|
name="JobButton3",
|
|
2303
2354
|
text="JobButton3",
|
|
2304
|
-
job=
|
|
2355
|
+
job=jbr_complex,
|
|
2305
2356
|
confirmation=True,
|
|
2306
2357
|
weight=50,
|
|
2307
2358
|
),
|
|
@@ -2315,7 +2366,7 @@ class JobButtonTestCase(
|
|
|
2315
2366
|
"content_types": [location_ct.pk],
|
|
2316
2367
|
"name": "jobbutton-4",
|
|
2317
2368
|
"text": "jobbutton text 4",
|
|
2318
|
-
"job":
|
|
2369
|
+
"job": jbr_complex.pk,
|
|
2319
2370
|
"weight": 100,
|
|
2320
2371
|
"button_class": "default",
|
|
2321
2372
|
"confirmation": False,
|
|
@@ -2330,6 +2381,9 @@ class JobButtonRenderingTestCase(TestCase):
|
|
|
2330
2381
|
def setUp(self):
|
|
2331
2382
|
super().setUp()
|
|
2332
2383
|
self.job = Job.objects.get(job_class_name="TestJobButtonReceiverSimple")
|
|
2384
|
+
self.job.enabled = True
|
|
2385
|
+
self.job.save()
|
|
2386
|
+
|
|
2333
2387
|
self.job_button_1 = JobButton(
|
|
2334
2388
|
name="JobButton 1",
|
|
2335
2389
|
text="JobButton {{ obj.name }}",
|
|
@@ -2339,10 +2393,14 @@ class JobButtonRenderingTestCase(TestCase):
|
|
|
2339
2393
|
self.job_button_1.validated_save()
|
|
2340
2394
|
self.job_button_1.content_types.add(ContentType.objects.get_for_model(LocationType))
|
|
2341
2395
|
|
|
2396
|
+
job_2 = Job.objects.get(job_class_name="TestJobButtonReceiverComplex")
|
|
2397
|
+
job_2.enabled = True
|
|
2398
|
+
job_2.save()
|
|
2399
|
+
|
|
2342
2400
|
self.job_button_2 = JobButton(
|
|
2343
2401
|
name="JobButton 2",
|
|
2344
2402
|
text="Click me!",
|
|
2345
|
-
job=
|
|
2403
|
+
job=job_2,
|
|
2346
2404
|
confirmation=False,
|
|
2347
2405
|
)
|
|
2348
2406
|
self.job_button_2.validated_save()
|
nautobot/extras/views.py
CHANGED
|
@@ -56,8 +56,6 @@ from .datasources import (
|
|
|
56
56
|
enqueue_pull_git_repository_and_refresh_data,
|
|
57
57
|
get_datasource_contents,
|
|
58
58
|
)
|
|
59
|
-
from .filters import RoleFilterSet
|
|
60
|
-
from .forms import RoleBulkEditForm, RoleForm
|
|
61
59
|
from .jobs import get_job
|
|
62
60
|
from .models import (
|
|
63
61
|
ComputedField,
|
|
@@ -94,7 +92,6 @@ from .models import (
|
|
|
94
92
|
Webhook,
|
|
95
93
|
)
|
|
96
94
|
from .registry import registry
|
|
97
|
-
from .tables import AssociatedContactsTable, RoleTable
|
|
98
95
|
|
|
99
96
|
logger = logging.getLogger(__name__)
|
|
100
97
|
|
|
@@ -398,7 +395,7 @@ class ContactAssociationUIViewSet(
|
|
|
398
395
|
filterset_class = filters.ContactAssociationFilterSet
|
|
399
396
|
queryset = ContactAssociation.objects.all()
|
|
400
397
|
serializer_class = serializers.ContactAssociationSerializer
|
|
401
|
-
table_class = AssociatedContactsTable
|
|
398
|
+
table_class = tables.AssociatedContactsTable
|
|
402
399
|
non_filter_params = ("export", "page", "per_page", "sort")
|
|
403
400
|
|
|
404
401
|
|
|
@@ -506,6 +503,7 @@ class CustomFieldListView(generic.ObjectListView):
|
|
|
506
503
|
queryset = CustomField.objects.all()
|
|
507
504
|
table = tables.CustomFieldTable
|
|
508
505
|
filterset = filters.CustomFieldFilterSet
|
|
506
|
+
filterset_form = forms.CustomFieldFilterForm
|
|
509
507
|
action_buttons = ("add",)
|
|
510
508
|
|
|
511
509
|
|
|
@@ -706,7 +704,7 @@ class DynamicGroupView(generic.ObjectView):
|
|
|
706
704
|
|
|
707
705
|
if table_class is not None:
|
|
708
706
|
# Members table (for display on Members nav tab)
|
|
709
|
-
members_table = table_class(instance.members, orderable=False)
|
|
707
|
+
members_table = table_class(instance.members.restrict(request.user, "view"), orderable=False)
|
|
710
708
|
paginate = {
|
|
711
709
|
"paginator_class": EnhancedPaginator,
|
|
712
710
|
"per_page": get_paginate_count(request),
|
|
@@ -886,7 +884,9 @@ class ObjectDynamicGroupsView(generic.GenericView):
|
|
|
886
884
|
obj = get_object_or_404(model, **kwargs)
|
|
887
885
|
|
|
888
886
|
# Gather all dynamic groups for this object (and its related objects)
|
|
889
|
-
dynamicsgroups_table = tables.DynamicGroupTable(
|
|
887
|
+
dynamicsgroups_table = tables.DynamicGroupTable(
|
|
888
|
+
data=obj.dynamic_groups_cached.restrict(request.user, "view"), orderable=False
|
|
889
|
+
)
|
|
890
890
|
|
|
891
891
|
# Apply the request context
|
|
892
892
|
paginate = {
|
|
@@ -950,6 +950,7 @@ class ExportTemplateBulkDeleteView(generic.BulkDeleteView):
|
|
|
950
950
|
class ExternalIntegrationUIViewSet(NautobotUIViewSet):
|
|
951
951
|
bulk_update_form_class = forms.ExternalIntegrationBulkEditForm
|
|
952
952
|
filterset_class = filters.ExternalIntegrationFilterSet
|
|
953
|
+
filterset_form_class = forms.ExternalIntegrationFilterForm
|
|
953
954
|
form_class = forms.ExternalIntegrationForm
|
|
954
955
|
queryset = ExternalIntegration.objects.select_related("secrets_group")
|
|
955
956
|
serializer_class = serializers.ExternalIntegrationSerializer
|
|
@@ -2048,11 +2049,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
|
|
|
2048
2049
|
"""`Roles` UIViewSet."""
|
|
2049
2050
|
|
|
2050
2051
|
queryset = Role.objects.all()
|
|
2051
|
-
bulk_update_form_class = RoleBulkEditForm
|
|
2052
|
-
filterset_class = RoleFilterSet
|
|
2053
|
-
|
|
2052
|
+
bulk_update_form_class = forms.RoleBulkEditForm
|
|
2053
|
+
filterset_class = filters.RoleFilterSet
|
|
2054
|
+
filterset_form_class = forms.RoleFilterForm
|
|
2055
|
+
form_class = forms.RoleForm
|
|
2054
2056
|
serializer_class = serializers.RoleSerializer
|
|
2055
|
-
table_class = RoleTable
|
|
2057
|
+
table_class = tables.RoleTable
|
|
2056
2058
|
|
|
2057
2059
|
def get_extra_context(self, request, instance):
|
|
2058
2060
|
context = super().get_extra_context(request, instance)
|
nautobot/ipam/filters.py
CHANGED
|
@@ -433,11 +433,19 @@ class IPAddressFilterSet(
|
|
|
433
433
|
method="_has_interface_assignments",
|
|
434
434
|
label="Has Interface Assignments",
|
|
435
435
|
)
|
|
436
|
+
nat_inside = django_filters.ModelMultipleChoiceFilter(
|
|
437
|
+
queryset=IPAddress.objects.all(),
|
|
438
|
+
label="NAT (Inside)",
|
|
439
|
+
)
|
|
440
|
+
has_nat_inside = RelatedMembershipBooleanFilter(
|
|
441
|
+
field_name="nat_inside",
|
|
442
|
+
label="Has NAT Inside",
|
|
443
|
+
)
|
|
436
444
|
ip_version = django_filters.NumberFilter()
|
|
437
445
|
|
|
438
446
|
class Meta:
|
|
439
447
|
model = IPAddress
|
|
440
|
-
fields = ["id", "dns_name", "type", "tags", "mask_length"]
|
|
448
|
+
fields = ["id", "dns_name", "type", "tags", "mask_length", "nat_inside"]
|
|
441
449
|
|
|
442
450
|
def generate_query__has_interface_assignments(self, value):
|
|
443
451
|
"""Helper method used by DynamicGroups and by _assigned_to_interface method."""
|
nautobot/ipam/forms.py
CHANGED
|
@@ -94,6 +94,12 @@ class NamespaceBulkEditForm(
|
|
|
94
94
|
]
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
class NamespaceFilterForm(LocatableModelFilterFormMixin, NautobotFilterForm):
|
|
98
|
+
model = Namespace
|
|
99
|
+
q = forms.CharField(required=False, label="Search")
|
|
100
|
+
name = forms.CharField(required=False)
|
|
101
|
+
|
|
102
|
+
|
|
97
103
|
#
|
|
98
104
|
# VRFs
|
|
99
105
|
#
|
|
@@ -142,6 +148,12 @@ class VRFBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
|
142
148
|
namespace = DynamicModelChoiceField(queryset=Namespace.objects.all(), required=False)
|
|
143
149
|
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
144
150
|
description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
|
|
151
|
+
add_prefixes = DynamicModelMultipleChoiceField(
|
|
152
|
+
queryset=Prefix.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
153
|
+
)
|
|
154
|
+
remove_prefixes = DynamicModelMultipleChoiceField(
|
|
155
|
+
queryset=Prefix.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
156
|
+
)
|
|
145
157
|
|
|
146
158
|
class Meta:
|
|
147
159
|
nullable_fields = [
|
|
@@ -358,6 +370,12 @@ class PrefixBulkEditForm(
|
|
|
358
370
|
remove_locations = DynamicModelMultipleChoiceField(
|
|
359
371
|
queryset=Location.objects.all(), required=False, query_params={"content_type": Prefix._meta.label_lower}
|
|
360
372
|
)
|
|
373
|
+
add_vrfs = DynamicModelMultipleChoiceField(
|
|
374
|
+
queryset=VRF.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
375
|
+
)
|
|
376
|
+
remove_vrfs = DynamicModelMultipleChoiceField(
|
|
377
|
+
queryset=VRF.objects.all(), required=False, query_params={"namespace": "$namespace"}
|
|
378
|
+
)
|
|
361
379
|
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
362
380
|
rir = DynamicModelChoiceField(queryset=RIR.objects.all(), required=False, label="RIR")
|
|
363
381
|
date_allocated = forms.DateTimeField(required=False, widget=DateTimePicker)
|
|
@@ -646,6 +664,8 @@ class IPAddressFilterForm(NautobotFilterForm, TenancyFilterForm, StatusModelFilt
|
|
|
646
664
|
"role",
|
|
647
665
|
"tenant_group",
|
|
648
666
|
"tenant",
|
|
667
|
+
"nat_inside",
|
|
668
|
+
"has_nat_inside",
|
|
649
669
|
]
|
|
650
670
|
q = forms.CharField(required=False, label="Search")
|
|
651
671
|
parent = forms.CharField(
|
|
@@ -682,6 +702,12 @@ class IPAddressFilterForm(NautobotFilterForm, TenancyFilterForm, StatusModelFilt
|
|
|
682
702
|
widget=StaticSelect2(),
|
|
683
703
|
)
|
|
684
704
|
tags = TagFilterField(model)
|
|
705
|
+
nat_inside = DynamicModelChoiceField(queryset=IPAddress.objects.all(), required=False, label="NAT Inside Address")
|
|
706
|
+
has_nat_inside = forms.NullBooleanField(
|
|
707
|
+
required=False,
|
|
708
|
+
label="Has NAT Inside",
|
|
709
|
+
widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES),
|
|
710
|
+
)
|
|
685
711
|
|
|
686
712
|
|
|
687
713
|
#
|
nautobot/ipam/tables.py
CHANGED
|
@@ -473,7 +473,7 @@ class IPAddressTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
473
473
|
|
|
474
474
|
|
|
475
475
|
class IPAddressDetailTable(IPAddressTable):
|
|
476
|
-
nat_inside = tables.Column(linkify=True,
|
|
476
|
+
nat_inside = tables.Column(linkify=True, verbose_name="NAT (Inside)")
|
|
477
477
|
tenant = TenantColumn()
|
|
478
478
|
tags = TagColumn(url_name="ipam:ipaddress_list")
|
|
479
479
|
assigned = BooleanColumn(accessor="assigned_count")
|
|
@@ -462,6 +462,7 @@ class IPAddressTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyF
|
|
|
462
462
|
queryset = IPAddress.objects.all()
|
|
463
463
|
filterset = IPAddressFilterSet
|
|
464
464
|
tenancy_related_name = "ip_addresses"
|
|
465
|
+
generic_filter_tests = (["nat_inside", "nat_inside__id"],)
|
|
465
466
|
|
|
466
467
|
@classmethod
|
|
467
468
|
def setUpTestData(cls):
|
|
@@ -638,6 +639,20 @@ class IPAddressTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyF
|
|
|
638
639
|
status=statuses[0],
|
|
639
640
|
namespace=cls.namespace,
|
|
640
641
|
)
|
|
642
|
+
IPAddress.objects.create(
|
|
643
|
+
address="10.1.1.1/32",
|
|
644
|
+
tenant=None,
|
|
645
|
+
status=statuses[0],
|
|
646
|
+
namespace=cls.namespace,
|
|
647
|
+
nat_inside=ip0,
|
|
648
|
+
)
|
|
649
|
+
IPAddress.objects.create(
|
|
650
|
+
address="10.2.2.2/32",
|
|
651
|
+
tenant=None,
|
|
652
|
+
status=statuses[0],
|
|
653
|
+
namespace=cls.namespace,
|
|
654
|
+
nat_inside=ip1,
|
|
655
|
+
)
|
|
641
656
|
|
|
642
657
|
def test_search(self):
|
|
643
658
|
ipv4_octets = self.ipv4_address.host.split(".")
|
|
@@ -71,7 +71,8 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
71
71
|
@classmethod
|
|
72
72
|
def setUpTestData(cls):
|
|
73
73
|
tenants = Tenant.objects.all()[:2]
|
|
74
|
-
namespace =
|
|
74
|
+
namespace = Prefix.objects.first().namespace
|
|
75
|
+
prefixes = Prefix.objects.filter(namespace=namespace)
|
|
75
76
|
|
|
76
77
|
cls.form_data = {
|
|
77
78
|
"name": "VRF X",
|
|
@@ -79,12 +80,16 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
79
80
|
"rd": "65000:999",
|
|
80
81
|
"tenant": tenants[0].pk,
|
|
81
82
|
"description": "A new VRF",
|
|
83
|
+
"prefixes": [prefixes[1].id],
|
|
82
84
|
"tags": [t.pk for t in Tag.objects.get_for_model(VRF)],
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
cls.bulk_edit_data = {
|
|
86
88
|
"tenant": tenants[1].pk,
|
|
87
89
|
"description": "New description",
|
|
90
|
+
"namespace": prefixes[0].namespace.id,
|
|
91
|
+
"add_prefixes": [prefixes[0].id],
|
|
92
|
+
"remove_prefixes": [prefixes[1].id],
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
|
|
@@ -158,7 +163,6 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
|
|
|
158
163
|
|
|
159
164
|
cls.bulk_edit_data = {
|
|
160
165
|
"tenant": None,
|
|
161
|
-
# TODO "vrf": vrfs[1].pk,
|
|
162
166
|
"status": cls.statuses[1].pk,
|
|
163
167
|
"role": cls.roles[1].pk,
|
|
164
168
|
"rir": RIR.objects.last().pk,
|
|
@@ -166,6 +170,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
|
|
|
166
170
|
"description": "New description",
|
|
167
171
|
"add_locations": [cls.locations[0].pk],
|
|
168
172
|
"remove_locations": [cls.locations[1].pk],
|
|
173
|
+
"namespace": vrfs[0].namespace.pk,
|
|
174
|
+
"add_vrfs": [vrfs[0].pk],
|
|
175
|
+
"remove_vrfs": [vrfs[1].pk],
|
|
169
176
|
}
|
|
170
177
|
|
|
171
178
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
nautobot/ipam/views.py
CHANGED
|
@@ -80,6 +80,7 @@ class NamespaceUIViewSet(
|
|
|
80
80
|
form_class = forms.NamespaceForm
|
|
81
81
|
bulk_update_form_class = forms.NamespaceBulkEditForm
|
|
82
82
|
filterset_class = filters.NamespaceFilterSet
|
|
83
|
+
filterset_form_class = forms.NamespaceFilterForm
|
|
83
84
|
queryset = Namespace.objects.all()
|
|
84
85
|
serializer_class = serializers.NamespaceSerializer
|
|
85
86
|
table_class = tables.NamespaceTable
|
|
@@ -303,6 +304,12 @@ class VRFBulkEditView(generic.BulkEditView):
|
|
|
303
304
|
table = tables.VRFTable
|
|
304
305
|
form = forms.VRFBulkEditForm
|
|
305
306
|
|
|
307
|
+
def extra_post_save_action(self, obj, form):
|
|
308
|
+
if form.cleaned_data.get("add_prefixes", None):
|
|
309
|
+
obj.prefixes.add(*form.cleaned_data["add_prefixes"])
|
|
310
|
+
if form.cleaned_data.get("remove_prefixes", None):
|
|
311
|
+
obj.prefixes.remove(*form.cleaned_data["remove_prefixes"])
|
|
312
|
+
|
|
306
313
|
|
|
307
314
|
class VRFBulkDeleteView(generic.BulkDeleteView):
|
|
308
315
|
queryset = VRF.objects.select_related("tenant")
|
|
@@ -713,6 +720,10 @@ class PrefixBulkEditView(generic.BulkEditView):
|
|
|
713
720
|
obj.locations.add(*form.cleaned_data["add_locations"])
|
|
714
721
|
if form.cleaned_data.get("remove_locations", None):
|
|
715
722
|
obj.locations.remove(*form.cleaned_data["remove_locations"])
|
|
723
|
+
if form.cleaned_data.get("add_vrfs", None):
|
|
724
|
+
obj.vrfs.add(*form.cleaned_data["add_vrfs"])
|
|
725
|
+
if form.cleaned_data.get("remove_vrfs", None):
|
|
726
|
+
obj.vrfs.remove(*form.cleaned_data["remove_vrfs"])
|
|
716
727
|
|
|
717
728
|
|
|
718
729
|
class PrefixBulkDeleteView(generic.BulkDeleteView):
|