nautobot 2.4.0__py3-none-any.whl → 2.4.0b1__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/__init__.py +1 -1
- nautobot/apps/api.py +8 -8
- nautobot/apps/change_logging.py +2 -2
- nautobot/apps/choices.py +4 -4
- nautobot/apps/events.py +3 -3
- nautobot/apps/factory.py +2 -2
- nautobot/apps/filters.py +1 -1
- nautobot/apps/forms.py +20 -20
- nautobot/apps/graphql.py +2 -2
- nautobot/apps/jobs.py +8 -8
- nautobot/apps/models.py +19 -19
- nautobot/apps/tables.py +1 -1
- nautobot/apps/testing.py +10 -10
- nautobot/apps/ui.py +2 -2
- nautobot/apps/utils.py +7 -7
- nautobot/apps/views.py +7 -7
- nautobot/circuits/api/serializers.py +0 -1
- nautobot/circuits/api/views.py +8 -4
- nautobot/circuits/tables.py +1 -2
- nautobot/circuits/templates/circuits/circuit_create.html +7 -1
- nautobot/circuits/views.py +3 -3
- nautobot/cloud/api/views.py +10 -6
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/views.py +16 -0
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/filter_backends.py +9 -3
- nautobot/core/api/schema.py +2 -13
- nautobot/core/api/serializers.py +34 -40
- nautobot/core/api/views.py +4 -56
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +1 -1
- nautobot/core/choices.py +2 -2
- nautobot/core/events/__init__.py +3 -3
- nautobot/core/filters.py +16 -21
- nautobot/core/fixtures/user-data.json +59 -0
- nautobot/core/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +11 -14
- nautobot/core/forms/forms.py +2 -33
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +7 -28
- nautobot/core/jobs/cleanup.py +12 -48
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +3 -6
- nautobot/core/settings.py +2 -29
- nautobot/core/settings.yaml +0 -21
- nautobot/core/tables.py +60 -74
- nautobot/core/templates/inc/media.html +0 -3
- nautobot/core/templates/inc/nav_menu.html +0 -1
- nautobot/core/templates/nautobot_config.py.j2 +0 -13
- nautobot/core/templates/search.html +0 -7
- nautobot/core/templates/utilities/render_jinja2.html +1 -1
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +0 -7
- nautobot/core/templatetags/helpers.py +2 -11
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +15 -170
- nautobot/core/testing/filters.py +2 -25
- nautobot/core/testing/forms.py +0 -2
- nautobot/core/testing/mixins.py +2 -7
- nautobot/core/testing/views.py +29 -44
- nautobot/core/tests/integration/test_app_home.py +1 -0
- nautobot/core/tests/integration/test_app_navbar.py +1 -0
- nautobot/core/tests/integration/test_filters.py +2 -0
- nautobot/core/tests/integration/test_home.py +1 -0
- nautobot/core/tests/integration/test_navbar.py +1 -0
- nautobot/core/tests/integration/test_view_authentication.py +0 -1
- nautobot/core/tests/runner.py +1 -1
- nautobot/core/tests/test_api.py +1 -98
- nautobot/core/tests/test_csv.py +3 -25
- nautobot/core/tests/test_forms.py +0 -1
- nautobot/core/tests/test_jobs.py +1 -303
- nautobot/core/tests/test_settings_schema.py +0 -7
- nautobot/core/tests/test_tables.py +0 -100
- nautobot/core/tests/test_utils.py +1 -63
- nautobot/core/tests/test_views.py +3 -30
- nautobot/core/ui/nav.py +0 -1
- nautobot/core/ui/object_detail.py +1 -15
- nautobot/core/urls.py +0 -11
- nautobot/core/utils/lookup.py +8 -11
- nautobot/core/utils/requests.py +9 -24
- nautobot/core/views/__init__.py +0 -42
- nautobot/core/views/generic.py +176 -78
- nautobot/core/views/mixins.py +34 -94
- nautobot/core/views/renderers.py +6 -6
- nautobot/dcim/api/serializers.py +62 -54
- nautobot/dcim/api/views.py +113 -47
- nautobot/dcim/filters/__init__.py +2 -31
- nautobot/dcim/forms.py +17 -36
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/models/__init__.py +1 -1
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +20 -22
- nautobot/dcim/models/devices.py +1 -10
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +5 -6
- nautobot/dcim/models/racks.py +4 -4
- nautobot/dcim/tables/__init__.py +3 -3
- nautobot/dcim/tables/devices.py +5 -7
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller_create.html +7 -1
- nautobot/dcim/templates/dcim/controller_retrieve.html +9 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -5
- nautobot/dcim/templates/dcim/device.html +9 -1
- nautobot/dcim/templates/dcim/device_edit.html +37 -36
- nautobot/dcim/templates/dcim/location.html +9 -1
- nautobot/dcim/templates/dcim/location_edit.html +7 -1
- nautobot/dcim/templates/dcim/rack.html +9 -1
- nautobot/dcim/templates/dcim/rack_edit.html +7 -1
- nautobot/dcim/templates/dcim/rackreservation.html +9 -1
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +9 -1
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +7 -1
- nautobot/dcim/tests/test_api.py +3 -16
- nautobot/dcim/tests/test_filters.py +0 -33
- nautobot/dcim/tests/test_forms.py +2 -51
- nautobot/dcim/tests/test_graphql.py +0 -52
- nautobot/dcim/tests/test_models.py +5 -34
- nautobot/dcim/tests/test_views.py +83 -21
- nautobot/dcim/views.py +13 -1
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +85 -90
- nautobot/extras/api/views.py +27 -22
- nautobot/extras/constants.py +0 -2
- nautobot/extras/filters/__init__.py +6 -8
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +31 -139
- nautobot/extras/forms/mixins.py +5 -13
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +2 -1
- nautobot/extras/jobs.py +12 -70
- nautobot/extras/managers.py +1 -3
- nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
- nautobot/extras/models/__init__.py +1 -1
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/customfields.py +11 -12
- nautobot/extras/models/groups.py +9 -11
- nautobot/extras/models/jobs.py +4 -23
- nautobot/extras/models/models.py +2 -2
- nautobot/extras/plugins/__init__.py +2 -13
- nautobot/extras/plugins/marketplace_manifest.yml +79 -84
- nautobot/extras/plugins/tables.py +14 -16
- nautobot/extras/plugins/views.py +69 -65
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/tables.py +5 -7
- nautobot/extras/templates/extras/dynamicgroup.html +9 -1
- nautobot/extras/templates/extras/job_detail.html +0 -16
- nautobot/extras/templates/extras/job_edit.html +0 -1
- nautobot/extras/templates/extras/jobqueue_retrieve.html +9 -1
- nautobot/extras/templates/extras/marketplace.html +11 -29
- nautobot/extras/templates/extras/plugin_detail.html +15 -32
- nautobot/extras/templates/extras/plugins_tiles.html +10 -21
- nautobot/extras/test_jobs/api_test_job.py +1 -1
- nautobot/extras/test_jobs/atomic_transaction.py +2 -2
- nautobot/extras/test_jobs/dry_run.py +1 -1
- nautobot/extras/test_jobs/fail.py +5 -5
- nautobot/extras/test_jobs/file_output.py +1 -1
- nautobot/extras/test_jobs/file_upload_fail.py +1 -1
- nautobot/extras/test_jobs/file_upload_pass.py +1 -1
- nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
- nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
- nautobot/extras/test_jobs/log_redaction.py +1 -1
- nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
- nautobot/extras/test_jobs/modify_db.py +1 -1
- nautobot/extras/test_jobs/object_var_optional.py +1 -1
- nautobot/extras/test_jobs/object_var_required.py +1 -1
- nautobot/extras/test_jobs/object_vars.py +1 -1
- nautobot/extras/test_jobs/pass.py +3 -3
- nautobot/extras/test_jobs/profiling.py +1 -1
- nautobot/extras/test_jobs/relative_import.py +3 -3
- nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
- nautobot/extras/test_jobs/task_queues.py +1 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
- nautobot/extras/tests/test_api.py +13 -13
- nautobot/extras/tests/test_customfields.py +1 -1
- nautobot/extras/tests/test_datasources.py +1 -2
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_filters.py +6 -6
- nautobot/extras/tests/test_forms.py +1 -20
- nautobot/extras/tests/test_jobs.py +19 -160
- nautobot/extras/tests/test_models.py +10 -10
- nautobot/extras/tests/test_plugins.py +9 -62
- nautobot/extras/tests/test_relationships.py +9 -120
- nautobot/extras/tests/test_views.py +191 -52
- nautobot/extras/utils.py +2 -3
- nautobot/extras/views.py +98 -30
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +33 -41
- nautobot/ipam/api/views.py +117 -68
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +2 -3
- nautobot/ipam/lookups.py +62 -101
- nautobot/ipam/models.py +16 -66
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +7 -23
- nautobot/ipam/templates/ipam/ipaddress.html +9 -1
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +7 -1
- nautobot/ipam/templates/ipam/ipaddress_edit.html +7 -1
- nautobot/ipam/templates/ipam/prefix.html +9 -1
- nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
- nautobot/ipam/templates/ipam/vlan.html +9 -1
- nautobot/ipam/templates/ipam/vlan_edit.html +7 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +7 -1
- nautobot/ipam/tests/test_api.py +3 -416
- nautobot/ipam/tests/test_forms.py +47 -49
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +34 -95
- nautobot/ipam/tests/test_querysets.py +1 -63
- nautobot/ipam/tests/test_views.py +0 -3
- nautobot/ipam/utils/__init__.py +6 -36
- nautobot/ipam/views.py +87 -61
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +2 -40
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
- nautobot/project-static/docs/404.html +4 -46
- nautobot/project-static/docs/apps/index.html +4 -46
- nautobot/project-static/docs/apps/nautobot-apps.html +6 -47
- nautobot/project-static/docs/assets/_mkdocstrings.css +1 -25
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +10 -62
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +7 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +122 -374
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -90
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +21 -95
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +6 -53
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +5 -52
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +17 -79
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +28 -102
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +21 -108
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +38 -131
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +65 -239
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +165 -581
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +36 -109
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +167 -453
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +211 -493
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +8 -60
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +15 -71
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +55 -407
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +205 -585
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +412 -858
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +7 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +186 -448
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +147 -365
- nautobot/project-static/docs/development/apps/api/configuration-view.html +4 -46
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/global-search.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/graphql.html +4 -46
- nautobot/project-static/docs/development/apps/api/models/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +4 -46
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +7 -68
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +4 -46
- nautobot/project-static/docs/development/apps/api/prometheus.html +4 -46
- nautobot/project-static/docs/development/apps/api/setup.html +4 -46
- nautobot/project-static/docs/development/apps/api/testing.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +4 -46
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/base-template.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/index.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/notes.html +4 -46
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +6 -52
- nautobot/project-static/docs/development/apps/api/views/urls.html +4 -46
- nautobot/project-static/docs/development/apps/index.html +4 -46
- nautobot/project-static/docs/development/apps/migration/code-updates.html +4 -46
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +4 -46
- nautobot/project-static/docs/development/apps/migration/from-v1.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +4 -46
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +4 -46
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +8 -50
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +4 -46
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +14 -211
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +4 -46
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +4 -46
- nautobot/project-static/docs/development/core/application-registry.html +4 -46
- nautobot/project-static/docs/development/core/best-practices.html +4 -46
- nautobot/project-static/docs/development/core/bootstrap-ui.html +4 -46
- nautobot/project-static/docs/development/core/caching.html +4 -46
- nautobot/project-static/docs/development/core/controllers.html +4 -46
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +74 -73
- nautobot/project-static/docs/development/core/generic-views.html +4 -46
- nautobot/project-static/docs/development/core/getting-started.html +224 -249
- nautobot/project-static/docs/development/core/homepage.html +7 -49
- nautobot/project-static/docs/development/core/index.html +4 -46
- nautobot/project-static/docs/development/core/{minikube-dev-environment-for-k8s-jobs.html → local-k8s.html} +168 -469
- nautobot/project-static/docs/development/core/model-checklist.html +12 -56
- nautobot/project-static/docs/development/core/model-features.html +4 -46
- nautobot/project-static/docs/development/core/natural-keys.html +4 -46
- nautobot/project-static/docs/development/core/navigation-menu.html +4 -46
- nautobot/project-static/docs/development/core/release-checklist.html +7 -49
- nautobot/project-static/docs/development/core/role-internals.html +4 -46
- nautobot/project-static/docs/development/core/settings.html +4 -46
- nautobot/project-static/docs/development/core/style-guide.html +7 -49
- nautobot/project-static/docs/development/core/templates.html +4 -46
- nautobot/project-static/docs/development/core/testing.html +4 -46
- nautobot/project-static/docs/development/core/ui-component-framework.html +273 -369
- nautobot/project-static/docs/development/core/user-preferences.html +4 -46
- nautobot/project-static/docs/development/index.html +4 -46
- nautobot/project-static/docs/development/jobs/index.html +122 -216
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +4 -46
- nautobot/project-static/docs/index.html +23 -54
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +7 -47
- nautobot/project-static/docs/overview/design_philosophy.html +4 -46
- nautobot/project-static/docs/release-notes/index.html +12 -52
- nautobot/project-static/docs/release-notes/version-1.0.html +193 -234
- nautobot/project-static/docs/release-notes/version-1.1.html +190 -231
- nautobot/project-static/docs/release-notes/version-1.2.html +265 -306
- nautobot/project-static/docs/release-notes/version-1.3.html +291 -332
- nautobot/project-static/docs/release-notes/version-1.4.html +377 -417
- nautobot/project-static/docs/release-notes/version-1.5.html +566 -605
- nautobot/project-static/docs/release-notes/version-1.6.html +447 -904
- nautobot/project-static/docs/release-notes/version-2.0.html +489 -528
- nautobot/project-static/docs/release-notes/version-2.1.html +324 -363
- nautobot/project-static/docs/release-notes/version-2.2.html +317 -356
- nautobot/project-static/docs/release-notes/version-2.3.html +352 -997
- nautobot/project-static/docs/release-notes/version-2.4.html +101 -417
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +287 -295
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +6 -48
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +4 -46
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +8 -110
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +4 -46
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +6 -48
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +8 -66
- nautobot/project-static/docs/user-guide/administration/installation/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +5 -47
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +4 -46
- nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -46
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +4 -46
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +4 -46
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +4 -46
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +8 -49
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +4 -46
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +12 -50
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +7 -49
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +4 -46
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +7 -51
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +4 -46
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +7 -49
- nautobot/project-static/docs/user-guide/index.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +8 -50
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +7 -50
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +7 -49
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +5 -47
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +25 -94
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +5 -74
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +4 -46
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +4 -46
- nautobot/project-static/js/forms.js +1 -1
- nautobot/tenancy/api/views.py +13 -9
- nautobot/tenancy/views.py +2 -4
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +4 -5
- nautobot/users/api/views.py +3 -3
- nautobot/virtualization/api/serializers.py +4 -4
- nautobot/virtualization/api/views.py +24 -5
- nautobot/virtualization/filters.py +3 -20
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +7 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +9 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +8 -2
- nautobot/virtualization/tests/test_filters.py +0 -17
- nautobot/wireless/filters.py +2 -2
- nautobot/wireless/forms.py +1 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +9 -1
- nautobot/wireless/tests/test_filters.py +1 -29
- nautobot/wireless/views.py +10 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +6 -6
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +543 -591
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
- nautobot/core/api/constants.py +0 -11
- nautobot/core/jobs/bulk_actions.py +0 -248
- nautobot/core/templates/about.html +0 -67
- nautobot/core/templates/inc/tenancy_form_panel.html +0 -9
- nautobot/core/templates/inc/tenant_table_row.html +0 -11
- nautobot/core/utils/querysets.py +0 -64
- nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +0 -25
- nautobot/dcim/tests/integration/test_controller.py +0 -62
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +0 -71
- nautobot/dcim/tests/test_jobs.py +0 -118
- nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +0 -22
- nautobot/extras/migrations/0121_alter_team_contacts.py +0 -17
- nautobot/extras/test_jobs/singleton.py +0 -16
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
- nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -9444
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -9722
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +0 -42
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -12,7 +12,7 @@ from nautobot.core.forms import (
|
|
|
12
12
|
DynamicModelMultipleChoiceField,
|
|
13
13
|
)
|
|
14
14
|
from nautobot.core.tables import RelationshipColumn
|
|
15
|
-
from nautobot.core.testing import
|
|
15
|
+
from nautobot.core.testing import TestCase
|
|
16
16
|
from nautobot.core.testing.models import ModelTestCases
|
|
17
17
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
18
18
|
from nautobot.dcim.forms import DeviceForm
|
|
@@ -29,22 +29,9 @@ from nautobot.dcim.models import (
|
|
|
29
29
|
)
|
|
30
30
|
from nautobot.dcim.tables import LocationTable
|
|
31
31
|
from nautobot.dcim.tests.test_views import create_test_device
|
|
32
|
-
from nautobot.extras.choices import
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
RelationshipRequiredSideChoices,
|
|
36
|
-
RelationshipSideChoices,
|
|
37
|
-
RelationshipTypeChoices,
|
|
38
|
-
)
|
|
39
|
-
from nautobot.extras.models import (
|
|
40
|
-
Relationship,
|
|
41
|
-
RelationshipAssociation,
|
|
42
|
-
Role,
|
|
43
|
-
Status,
|
|
44
|
-
)
|
|
45
|
-
from nautobot.extras.models.jobs import JobLogEntry
|
|
46
|
-
from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
|
|
47
|
-
from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
|
|
32
|
+
from nautobot.extras.choices import RelationshipRequiredSideChoices, RelationshipSideChoices, RelationshipTypeChoices
|
|
33
|
+
from nautobot.extras.models import Relationship, RelationshipAssociation, Role, Status
|
|
34
|
+
from nautobot.ipam.models import VLAN, VLANGroup
|
|
48
35
|
|
|
49
36
|
|
|
50
37
|
class RelationshipBaseTest:
|
|
@@ -367,24 +354,20 @@ class RelationshipTest(RelationshipBaseTest, ModelTestCases.BaseModelTestCase):
|
|
|
367
354
|
self.assertFalse(field.required)
|
|
368
355
|
self.assertIsInstance(field, DynamicModelMultipleChoiceField)
|
|
369
356
|
self.assertEqual(field.label, "My VLANs")
|
|
370
|
-
self.assertEqual(field.query_params, {
|
|
357
|
+
self.assertEqual(field.query_params, {})
|
|
371
358
|
|
|
372
359
|
field = self.m2m_1.to_form_field("destination")
|
|
373
360
|
self.assertFalse(field.required)
|
|
374
361
|
self.assertIsInstance(field, DynamicModelMultipleChoiceField)
|
|
375
362
|
self.assertEqual(field.label, "My Racks")
|
|
376
363
|
self.assertEqual(
|
|
377
|
-
field.query_params,
|
|
378
|
-
{
|
|
379
|
-
"location": [self.locations[0].name, self.locations[1].name, self.locations[2].name],
|
|
380
|
-
"exclude_m2m": "true",
|
|
381
|
-
},
|
|
364
|
+
field.query_params, {"location": [self.locations[0].name, self.locations[1].name, self.locations[2].name]}
|
|
382
365
|
)
|
|
383
366
|
|
|
384
367
|
field = self.m2ms_1.to_form_field("peer")
|
|
385
368
|
self.assertFalse(field.required)
|
|
386
369
|
self.assertIsInstance(field, DynamicModelMultipleChoiceField)
|
|
387
|
-
self.assertEqual(field.query_params, {
|
|
370
|
+
self.assertEqual(field.query_params, {})
|
|
388
371
|
|
|
389
372
|
def test_to_form_field_o2m(self):
|
|
390
373
|
field = self.o2m_1.to_form_field("source")
|
|
@@ -1343,7 +1326,7 @@ class RelationshipTableTest(RelationshipBaseTest, TestCase):
|
|
|
1343
1326
|
self.assertIsNotNone(relationship_column)
|
|
1344
1327
|
self.assertIsInstance(relationship_column, RelationshipColumn)
|
|
1345
1328
|
|
|
1346
|
-
rendered_value = bound_row.get_cell(internal_col_name)
|
|
1329
|
+
rendered_value = bound_row.get_cell(internal_col_name)
|
|
1347
1330
|
# Test if the expected value is in the rendered value.
|
|
1348
1331
|
# Exact match is difficult because the order of rendering is unpredictable.
|
|
1349
1332
|
for value in col_expected_value:
|
|
@@ -1449,7 +1432,7 @@ class RequiredRelationshipTestMixin:
|
|
|
1449
1432
|
required_on="source",
|
|
1450
1433
|
)
|
|
1451
1434
|
relationship_o2o.validated_save()
|
|
1452
|
-
vlan_group = VLANGroup.objects.
|
|
1435
|
+
vlan_group = VLANGroup.objects.first()
|
|
1453
1436
|
|
|
1454
1437
|
tests_params = [
|
|
1455
1438
|
# Required many-to-many:
|
|
@@ -1729,97 +1712,3 @@ class RequiredRelationshipTestMixin:
|
|
|
1729
1712
|
}
|
|
1730
1713
|
}
|
|
1731
1714
|
self.assertEqual(expected_error_json, response.json())
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
class RelationshipJobTestCase(RequiredRelationshipTestMixin, TransactionTestCase):
|
|
1735
|
-
databases = ("default", "job_logs")
|
|
1736
|
-
|
|
1737
|
-
def create_job(self, pk_list, **extra_form_data):
|
|
1738
|
-
""""""
|
|
1739
|
-
vlan_ct = ContentType.objects.get_for_model(VLAN)
|
|
1740
|
-
job_result = create_job_result_and_run_job(
|
|
1741
|
-
"nautobot.core.jobs.bulk_actions",
|
|
1742
|
-
"BulkEditObjects",
|
|
1743
|
-
content_type=vlan_ct.id,
|
|
1744
|
-
edit_all=False,
|
|
1745
|
-
filter_query_params={},
|
|
1746
|
-
form_data={"pk": pk_list, **extra_form_data},
|
|
1747
|
-
username=self.user.username,
|
|
1748
|
-
)
|
|
1749
|
-
return job_result
|
|
1750
|
-
|
|
1751
|
-
def test_required_relationships(self):
|
|
1752
|
-
"""
|
|
1753
|
-
1. Try creating an object when no required target object exists
|
|
1754
|
-
2. Try creating an object without specifying required target object(s)
|
|
1755
|
-
3. Try creating an object when all required data is present
|
|
1756
|
-
4. Test bulk edit
|
|
1757
|
-
"""
|
|
1758
|
-
|
|
1759
|
-
# Delete existing factory generated objects that may interfere with this test
|
|
1760
|
-
IPAddress.objects.all().delete()
|
|
1761
|
-
Prefix.objects.update(parent=None)
|
|
1762
|
-
Prefix.objects.all().delete()
|
|
1763
|
-
ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.all().delete()
|
|
1764
|
-
VLAN.objects.all().delete()
|
|
1765
|
-
|
|
1766
|
-
# Parameterized tests (for creating and updating single objects):
|
|
1767
|
-
self.required_relationships_test(interact_with="ui")
|
|
1768
|
-
|
|
1769
|
-
# 4. Bulk create/edit tests:
|
|
1770
|
-
|
|
1771
|
-
vlan_status = Status.objects.get_for_model(VLAN).first()
|
|
1772
|
-
vlans = (
|
|
1773
|
-
VLAN.objects.create(name="test_required_relationships1", vid=1, status=vlan_status),
|
|
1774
|
-
VLAN.objects.create(name="test_required_relationships2", vid=2, status=vlan_status),
|
|
1775
|
-
VLAN.objects.create(name="test_required_relationships3", vid=3, status=vlan_status),
|
|
1776
|
-
VLAN.objects.create(name="test_required_relationships4", vid=4, status=vlan_status),
|
|
1777
|
-
VLAN.objects.create(name="test_required_relationships5", vid=5, status=vlan_status),
|
|
1778
|
-
VLAN.objects.create(name="test_required_relationships6", vid=6, status=vlan_status),
|
|
1779
|
-
)
|
|
1780
|
-
|
|
1781
|
-
# Try deleting all devices and then editing the 6 VLANs (fails):
|
|
1782
|
-
Controller.objects.filter(controller_device__isnull=False).delete()
|
|
1783
|
-
Device.objects.all().delete()
|
|
1784
|
-
|
|
1785
|
-
pk_list = [str(vlan.id) for vlan in vlans]
|
|
1786
|
-
job_result = self.create_job(pk_list)
|
|
1787
|
-
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
|
|
1788
|
-
error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
1789
|
-
self.assertIn("VLANs require at least one device, but no devices exist yet.", error_log.message)
|
|
1790
|
-
|
|
1791
|
-
# Create test device for association
|
|
1792
|
-
device_for_association = create_test_device("VLAN Required Device")
|
|
1793
|
-
|
|
1794
|
-
# Try editing all 6 VLANs without adding the required device(fails):
|
|
1795
|
-
job_result = self.create_job(pk_list)
|
|
1796
|
-
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
|
|
1797
|
-
error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
1798
|
-
self.assertIn(
|
|
1799
|
-
'6 VLANs require a device for the required relationship \\"VLANs require at least one Device',
|
|
1800
|
-
error_log.message,
|
|
1801
|
-
)
|
|
1802
|
-
|
|
1803
|
-
# Try editing 3 VLANs without adding the required device(fails):
|
|
1804
|
-
job_result = self.create_job(pk_list[:3])
|
|
1805
|
-
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
|
|
1806
|
-
error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
1807
|
-
self.assertIn(
|
|
1808
|
-
'These VLANs require a device for the required relationship \\"VLANs require at least one Device',
|
|
1809
|
-
error_log.message,
|
|
1810
|
-
)
|
|
1811
|
-
for vlan in vlans[:3]:
|
|
1812
|
-
self.assertIn(str(vlan), error_log.message)
|
|
1813
|
-
|
|
1814
|
-
# Try editing 6 VLANs and adding the required device (succeeds):
|
|
1815
|
-
job_result = self.create_job(pk_list, add_cr_vlans_devices_m2m__source=[str(device_for_association.id)])
|
|
1816
|
-
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
|
|
1817
|
-
|
|
1818
|
-
# Try editing 6 VLANs and removing the required device (fails):
|
|
1819
|
-
job_result = self.create_job(pk_list, remove_cr_vlans_devices_m2m__source=[str(device_for_association.id)])
|
|
1820
|
-
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
|
|
1821
|
-
error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
1822
|
-
self.assertIn(
|
|
1823
|
-
'6 VLANs require a device for the required relationship \\"VLANs require at least one Device',
|
|
1824
|
-
error_log.message,
|
|
1825
|
-
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import timedelta
|
|
2
|
+
from http import HTTPStatus
|
|
2
3
|
import json
|
|
3
4
|
from unittest import mock
|
|
4
5
|
import urllib.parse
|
|
@@ -25,6 +26,7 @@ from nautobot.core.testing.utils import disable_warnings, get_deletable_objects,
|
|
|
25
26
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
26
27
|
from nautobot.dcim.models import (
|
|
27
28
|
ConsolePort,
|
|
29
|
+
Controller,
|
|
28
30
|
Device,
|
|
29
31
|
DeviceType,
|
|
30
32
|
Interface,
|
|
@@ -32,6 +34,7 @@ from nautobot.dcim.models import (
|
|
|
32
34
|
LocationType,
|
|
33
35
|
Manufacturer,
|
|
34
36
|
)
|
|
37
|
+
from nautobot.dcim.tests import test_views
|
|
35
38
|
from nautobot.extras.choices import (
|
|
36
39
|
CustomFieldTypeChoices,
|
|
37
40
|
DynamicGroupTypeChoices,
|
|
@@ -86,9 +89,10 @@ from nautobot.extras.templatetags.job_buttons import NO_CONFIRM_BUTTON
|
|
|
86
89
|
from nautobot.extras.tests.constants import BIG_GRAPHQL_DEVICE_QUERY
|
|
87
90
|
from nautobot.extras.tests.test_relationships import RequiredRelationshipTestMixin
|
|
88
91
|
from nautobot.extras.utils import RoleModelsQuery, TaggableClassesQuery
|
|
89
|
-
from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
|
|
92
|
+
from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
|
|
90
93
|
from nautobot.tenancy.models import Tenant
|
|
91
94
|
from nautobot.users.models import ObjectPermission
|
|
95
|
+
from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
|
|
92
96
|
|
|
93
97
|
# Use the proper swappable User model
|
|
94
98
|
User = get_user_model()
|
|
@@ -911,30 +915,6 @@ class DynamicGroupTestCase(
|
|
|
911
915
|
self.form_data["content_type"] = instance.content_type.pk # Content-type is not editable after creation
|
|
912
916
|
super().test_edit_object_with_constrained_permission()
|
|
913
917
|
|
|
914
|
-
def test_edit_object_with_content_type_ipam_prefix(self):
|
|
915
|
-
"""Assert bug fix #6526: `Error when defining Dynamic Group of Prefixes using `present_in_vrf_id` filter`"""
|
|
916
|
-
content_type = ContentType.objects.get_for_model(Prefix)
|
|
917
|
-
instance = DynamicGroup.objects.create(name="DG Ipam|Prefix", content_type=content_type)
|
|
918
|
-
vrf_instance = VRF.objects.first()
|
|
919
|
-
data = self.form_data.copy()
|
|
920
|
-
data.update(
|
|
921
|
-
{
|
|
922
|
-
"name": "DG Ipam|Prefix",
|
|
923
|
-
"content_type": content_type.pk,
|
|
924
|
-
"filter-present_in_vrf_id": vrf_instance.id,
|
|
925
|
-
"tenant": None,
|
|
926
|
-
"tags": [],
|
|
927
|
-
}
|
|
928
|
-
)
|
|
929
|
-
self.add_permissions("extras.change_dynamicgroup")
|
|
930
|
-
request = {
|
|
931
|
-
"path": self._get_url("edit", instance),
|
|
932
|
-
"data": post_data(data),
|
|
933
|
-
}
|
|
934
|
-
self.assertHttpStatus(self.client.post(**request), 302)
|
|
935
|
-
instance.refresh_from_db()
|
|
936
|
-
self.assertEqual(instance.filter["present_in_vrf_id"], str(vrf_instance.id))
|
|
937
|
-
|
|
938
918
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
939
919
|
def test_edit_saved_filter(self):
|
|
940
920
|
"""Test that editing a filter works using the edit view."""
|
|
@@ -1718,21 +1698,21 @@ class ScheduledJobTestCase(
|
|
|
1718
1698
|
user = User.objects.create(username="user1", is_active=True)
|
|
1719
1699
|
ScheduledJob.objects.create(
|
|
1720
1700
|
name="test1",
|
|
1721
|
-
task="pass.
|
|
1701
|
+
task="pass.TestPass",
|
|
1722
1702
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1723
1703
|
user=user,
|
|
1724
1704
|
start_time=timezone.now(),
|
|
1725
1705
|
)
|
|
1726
1706
|
ScheduledJob.objects.create(
|
|
1727
1707
|
name="test2",
|
|
1728
|
-
task="pass.
|
|
1708
|
+
task="pass.TestPass",
|
|
1729
1709
|
interval=JobExecutionType.TYPE_DAILY,
|
|
1730
1710
|
user=user,
|
|
1731
1711
|
start_time=timezone.now(),
|
|
1732
1712
|
)
|
|
1733
1713
|
ScheduledJob.objects.create(
|
|
1734
1714
|
name="test3",
|
|
1735
|
-
task="pass.
|
|
1715
|
+
task="pass.TestPass",
|
|
1736
1716
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1737
1717
|
user=user,
|
|
1738
1718
|
start_time=timezone.now(),
|
|
@@ -1746,7 +1726,7 @@ class ScheduledJobTestCase(
|
|
|
1746
1726
|
ScheduledJob.objects.create(
|
|
1747
1727
|
enabled=False,
|
|
1748
1728
|
name="test4",
|
|
1749
|
-
task="pass.
|
|
1729
|
+
task="pass.TestPass",
|
|
1750
1730
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1751
1731
|
user=self.user,
|
|
1752
1732
|
start_time=timezone.now(),
|
|
@@ -1763,7 +1743,7 @@ class ScheduledJobTestCase(
|
|
|
1763
1743
|
ScheduledJob.objects.create(
|
|
1764
1744
|
enabled=True,
|
|
1765
1745
|
name=name,
|
|
1766
|
-
task="pass.
|
|
1746
|
+
task="pass.TestPass",
|
|
1767
1747
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1768
1748
|
user=self.user,
|
|
1769
1749
|
start_time=timezone.now(),
|
|
@@ -1794,7 +1774,7 @@ class ScheduledJobTestCase(
|
|
|
1794
1774
|
ScheduledJob.objects.create(
|
|
1795
1775
|
enabled=True,
|
|
1796
1776
|
name="test11",
|
|
1797
|
-
task="pass.
|
|
1777
|
+
task="pass.TestPass",
|
|
1798
1778
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1799
1779
|
user=self.user,
|
|
1800
1780
|
start_time=timezone.now(),
|
|
@@ -1828,7 +1808,7 @@ class ApprovalQueueTestCase(
|
|
|
1828
1808
|
def setUp(self):
|
|
1829
1809
|
super().setUp()
|
|
1830
1810
|
self.job_model = Job.objects.get_for_class_path("dry_run.TestDryRun")
|
|
1831
|
-
self.job_model_2 = Job.objects.get_for_class_path("fail.
|
|
1811
|
+
self.job_model_2 = Job.objects.get_for_class_path("fail.TestFail")
|
|
1832
1812
|
|
|
1833
1813
|
ScheduledJob.objects.create(
|
|
1834
1814
|
name="test1",
|
|
@@ -1841,7 +1821,7 @@ class ApprovalQueueTestCase(
|
|
|
1841
1821
|
)
|
|
1842
1822
|
ScheduledJob.objects.create(
|
|
1843
1823
|
name="test2",
|
|
1844
|
-
task="fail.
|
|
1824
|
+
task="fail.TestFail",
|
|
1845
1825
|
job_model=self.job_model_2,
|
|
1846
1826
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1847
1827
|
user=self.user,
|
|
@@ -1854,7 +1834,7 @@ class ApprovalQueueTestCase(
|
|
|
1854
1834
|
|
|
1855
1835
|
ScheduledJob.objects.create(
|
|
1856
1836
|
name="test4",
|
|
1857
|
-
task="pass.
|
|
1837
|
+
task="pass.TestPass",
|
|
1858
1838
|
job_model=self.job_model,
|
|
1859
1839
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1860
1840
|
user=self.user,
|
|
@@ -2245,8 +2225,8 @@ class JobResultTestCase(
|
|
|
2245
2225
|
|
|
2246
2226
|
@classmethod
|
|
2247
2227
|
def setUpTestData(cls):
|
|
2248
|
-
JobResult.objects.create(name="pass.
|
|
2249
|
-
JobResult.objects.create(name="fail.
|
|
2228
|
+
JobResult.objects.create(name="pass.TestPass")
|
|
2229
|
+
JobResult.objects.create(name="fail.TestFail")
|
|
2250
2230
|
JobLogEntry.objects.create(
|
|
2251
2231
|
log_level=LogLevelChoices.LOG_INFO,
|
|
2252
2232
|
job_result=JobResult.objects.first(),
|
|
@@ -2300,7 +2280,7 @@ class JobTestCase(
|
|
|
2300
2280
|
# Job model objects are automatically created during database migrations
|
|
2301
2281
|
|
|
2302
2282
|
# But we do need to make sure the ones we're testing are flagged appropriately
|
|
2303
|
-
cls.test_pass = Job.objects.get(job_class_name="
|
|
2283
|
+
cls.test_pass = Job.objects.get(job_class_name="TestPass")
|
|
2304
2284
|
default_job_queue = JobQueue.objects.get(name="default", queue_type=JobQueueTypeChoices.TYPE_CELERY)
|
|
2305
2285
|
cls.test_pass.default_job_queue = default_job_queue
|
|
2306
2286
|
cls.test_pass.enabled = True
|
|
@@ -2436,6 +2416,35 @@ class JobTestCase(
|
|
|
2436
2416
|
# assert Job still exists
|
|
2437
2417
|
self.assertTrue(self._get_queryset().filter(name=job_name).exists())
|
|
2438
2418
|
|
|
2419
|
+
def test_bulk_delete_system_jobs_fail(self):
|
|
2420
|
+
system_job_queryset = self.model.objects.filter(module_name__startswith="nautobot.")
|
|
2421
|
+
pk_list = system_job_queryset.values_list("pk", flat=True)[:3]
|
|
2422
|
+
initial_count = self._get_queryset().count()
|
|
2423
|
+
data = {
|
|
2424
|
+
"pk": pk_list,
|
|
2425
|
+
"confirm": True,
|
|
2426
|
+
"_confirm": True, # Form button
|
|
2427
|
+
}
|
|
2428
|
+
# Try bulk delete with delete job permission
|
|
2429
|
+
self.add_permissions("extras.delete_job")
|
|
2430
|
+
response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
|
|
2431
|
+
self.assertBodyContains(
|
|
2432
|
+
response,
|
|
2433
|
+
f"Unable to delete Job {system_job_queryset.first()}. System Job cannot be deleted",
|
|
2434
|
+
status_code=403,
|
|
2435
|
+
)
|
|
2436
|
+
self.assertEqual(self._get_queryset().count(), initial_count)
|
|
2437
|
+
|
|
2438
|
+
# Try bulk delete as a superuser
|
|
2439
|
+
self.user.is_superuser = True
|
|
2440
|
+
response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
|
|
2441
|
+
self.assertBodyContains(
|
|
2442
|
+
response,
|
|
2443
|
+
f"Unable to delete Job {system_job_queryset.first()}. System Job cannot be deleted",
|
|
2444
|
+
status_code=403,
|
|
2445
|
+
)
|
|
2446
|
+
self.assertEqual(self._get_queryset().count(), initial_count)
|
|
2447
|
+
|
|
2439
2448
|
def validate_job_data_after_bulk_edit(self, pk_list, old_data):
|
|
2440
2449
|
# Name is bulk-editable
|
|
2441
2450
|
overridable_fields = [field for field in JOB_OVERRIDABLE_FIELDS if field != "name"]
|
|
@@ -2504,7 +2513,7 @@ class JobTestCase(
|
|
|
2504
2513
|
self.add_permissions("extras.run_job")
|
|
2505
2514
|
for run_url in self.run_urls:
|
|
2506
2515
|
response = self.client.get(run_url)
|
|
2507
|
-
self.assertBodyContains(response, "
|
|
2516
|
+
self.assertBodyContains(response, "TestPass")
|
|
2508
2517
|
|
|
2509
2518
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
2510
2519
|
def test_get_run_with_constrained_permission(self):
|
|
@@ -2603,12 +2612,12 @@ class JobTestCase(
|
|
|
2603
2612
|
self.add_permissions("extras.run_job")
|
|
2604
2613
|
|
|
2605
2614
|
for run_url in (
|
|
2606
|
-
reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.
|
|
2607
|
-
reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="
|
|
2615
|
+
reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.TestFail"}),
|
|
2616
|
+
reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="TestFail").pk}),
|
|
2608
2617
|
):
|
|
2609
2618
|
response = self.client.post(run_url, self.data_run_immediately)
|
|
2610
2619
|
self.assertBodyContains(response, "Job is not enabled to be run")
|
|
2611
|
-
self.assertFalse(JobResult.objects.filter(name="fail.
|
|
2620
|
+
self.assertFalse(JobResult.objects.filter(name="fail.TestFail").exists())
|
|
2612
2621
|
|
|
2613
2622
|
def test_run_now_missing_args(self):
|
|
2614
2623
|
self.add_permissions("extras.run_job")
|
|
@@ -2644,16 +2653,10 @@ class JobTestCase(
|
|
|
2644
2653
|
job_celery_kwargs = {
|
|
2645
2654
|
"nautobot_job_job_model_id": self.test_required_args.id,
|
|
2646
2655
|
"nautobot_job_profile": True,
|
|
2647
|
-
"nautobot_job_ignore_singleton_lock": True,
|
|
2648
2656
|
"nautobot_job_user_id": self.user.id,
|
|
2649
2657
|
"queue": job_queue.name,
|
|
2650
2658
|
}
|
|
2651
2659
|
self.test_required_args.job_queues.set([job_queue])
|
|
2652
|
-
self.test_required_args.is_singleton_override = True
|
|
2653
|
-
self.test_required_args.has_sensitive_variables_override = True
|
|
2654
|
-
self.test_required_args.is_singleton = True
|
|
2655
|
-
self.test_required_args.has_sensitive_variables = False
|
|
2656
|
-
self.test_required_args.validated_save()
|
|
2657
2660
|
previous_result = JobResult.objects.create(
|
|
2658
2661
|
job_model=self.test_required_args,
|
|
2659
2662
|
user=self.user,
|
|
@@ -2670,9 +2673,6 @@ class JobTestCase(
|
|
|
2670
2673
|
content,
|
|
2671
2674
|
)
|
|
2672
2675
|
self.assertInHTML('<input type="hidden" name="_profile" value="True" id="id__profile">', content)
|
|
2673
|
-
self.assertInHTML(
|
|
2674
|
-
'<input type="checkbox" name="_ignore_singleton_lock" id="id__ignore_singleton_lock" checked>', content
|
|
2675
|
-
)
|
|
2676
2676
|
|
|
2677
2677
|
@mock.patch("nautobot.extras.views.get_worker_count", return_value=1)
|
|
2678
2678
|
def test_run_later_missing_name(self, _):
|
|
@@ -2844,6 +2844,59 @@ class JobTestCase(
|
|
|
2844
2844
|
response = self.client.get(instance.get_changelog_url())
|
|
2845
2845
|
self.assertBodyContains(response, f"{instance.name} - Change Log")
|
|
2846
2846
|
|
|
2847
|
+
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
2848
|
+
def test_job_bulk_edit_default_queue_stays_default_after_one_field_update(self):
|
|
2849
|
+
self.add_permissions("extras.change_job")
|
|
2850
|
+
job_class_to_test = "TestPass"
|
|
2851
|
+
job_description = "default job queue test"
|
|
2852
|
+
job_to_test = self.model.objects.get(job_class_name=job_class_to_test)
|
|
2853
|
+
queryset = self.model.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
|
|
2854
|
+
default_job_queue = job_to_test.default_job_queue
|
|
2855
|
+
pk_list = list(queryset.values_list("pk", flat=True))
|
|
2856
|
+
|
|
2857
|
+
data = {
|
|
2858
|
+
"description": job_description,
|
|
2859
|
+
"pk": pk_list,
|
|
2860
|
+
"_apply": True,
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), HTTPStatus.FOUND)
|
|
2864
|
+
instance = self.model.objects.get(job_class_name=job_class_to_test)
|
|
2865
|
+
self.assertEqual(instance.description, job_description)
|
|
2866
|
+
self.assertEqual(instance.default_job_queue, default_job_queue)
|
|
2867
|
+
|
|
2868
|
+
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
2869
|
+
def test_job_bulk_edit_preserve_job_queues_after_one_field_update(self):
|
|
2870
|
+
self.add_permissions("extras.change_job")
|
|
2871
|
+
job_class_to_test = "TestPass"
|
|
2872
|
+
job_description = "job queues test"
|
|
2873
|
+
job_to_test = self.model.objects.get(job_class_name=job_class_to_test)
|
|
2874
|
+
|
|
2875
|
+
# Simulate 3 job queues set
|
|
2876
|
+
job_queues = JobQueue.objects.all()[:3]
|
|
2877
|
+
job_to_test.job_queues.set(job_queues)
|
|
2878
|
+
job_to_test.job_queues.add(job_to_test.default_job_queue)
|
|
2879
|
+
job_to_test.job_queues_override = True
|
|
2880
|
+
job_to_test.save()
|
|
2881
|
+
|
|
2882
|
+
expected_job_queues = list(job_to_test.job_queues.values_list("pk", flat=True))
|
|
2883
|
+
|
|
2884
|
+
queryset = self.model.objects.filter(installed=True, hidden=False, job_class_name=job_class_to_test)
|
|
2885
|
+
pk_list = list(queryset.values_list("pk", flat=True))
|
|
2886
|
+
|
|
2887
|
+
data = {
|
|
2888
|
+
"description": job_description,
|
|
2889
|
+
"pk": pk_list,
|
|
2890
|
+
"_apply": True,
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), HTTPStatus.FOUND)
|
|
2894
|
+
|
|
2895
|
+
instance = self.model.objects.get(job_class_name=job_class_to_test)
|
|
2896
|
+
self.assertEqual(instance.description, job_description)
|
|
2897
|
+
self.assertEqual(instance.job_queues.count(), 4) # 3 custom one + 1 default
|
|
2898
|
+
self.assertQuerySetEqual(instance.job_queues.all(), JobQueue.objects.filter(pk__in=expected_job_queues))
|
|
2899
|
+
|
|
2847
2900
|
|
|
2848
2901
|
class JobButtonTestCase(
|
|
2849
2902
|
ViewTestCases.CreateObjectViewTestCase,
|
|
@@ -3226,6 +3279,94 @@ class RelationshipTestCase(
|
|
|
3226
3279
|
|
|
3227
3280
|
cls.slug_test_object = "Primary Interface"
|
|
3228
3281
|
|
|
3282
|
+
def test_required_relationships(self):
|
|
3283
|
+
"""
|
|
3284
|
+
1. Try creating an object when no required target object exists
|
|
3285
|
+
2. Try creating an object without specifying required target object(s)
|
|
3286
|
+
3. Try creating an object when all required data is present
|
|
3287
|
+
4. Test bulk edit
|
|
3288
|
+
"""
|
|
3289
|
+
|
|
3290
|
+
# Delete existing factory generated objects that may interfere with this test
|
|
3291
|
+
IPAddress.objects.all().delete()
|
|
3292
|
+
Prefix.objects.update(parent=None)
|
|
3293
|
+
Prefix.objects.all().delete()
|
|
3294
|
+
ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.all().delete()
|
|
3295
|
+
VLAN.objects.all().delete()
|
|
3296
|
+
|
|
3297
|
+
# Parameterized tests (for creating and updating single objects):
|
|
3298
|
+
self.required_relationships_test(interact_with="ui")
|
|
3299
|
+
|
|
3300
|
+
# 4. Bulk create/edit tests:
|
|
3301
|
+
|
|
3302
|
+
vlan_status = Status.objects.get_for_model(VLAN).first()
|
|
3303
|
+
vlans = (
|
|
3304
|
+
VLAN.objects.create(name="test_required_relationships1", vid=1, status=vlan_status),
|
|
3305
|
+
VLAN.objects.create(name="test_required_relationships2", vid=2, status=vlan_status),
|
|
3306
|
+
VLAN.objects.create(name="test_required_relationships3", vid=3, status=vlan_status),
|
|
3307
|
+
VLAN.objects.create(name="test_required_relationships4", vid=4, status=vlan_status),
|
|
3308
|
+
VLAN.objects.create(name="test_required_relationships5", vid=5, status=vlan_status),
|
|
3309
|
+
VLAN.objects.create(name="test_required_relationships6", vid=6, status=vlan_status),
|
|
3310
|
+
)
|
|
3311
|
+
|
|
3312
|
+
# Try deleting all devices and then editing the 6 VLANs (fails):
|
|
3313
|
+
Controller.objects.filter(controller_device__isnull=False).delete()
|
|
3314
|
+
Device.objects.all().delete()
|
|
3315
|
+
response = self.client.post(
|
|
3316
|
+
reverse("ipam:vlan_bulk_edit"), data={"pk": [str(vlan.id) for vlan in vlans], "_apply": [""]}
|
|
3317
|
+
)
|
|
3318
|
+
self.assertContains(response, "VLANs require at least one device, but no devices exist yet.")
|
|
3319
|
+
|
|
3320
|
+
# Create test device for association
|
|
3321
|
+
device_for_association = test_views.create_test_device("VLAN Required Device")
|
|
3322
|
+
|
|
3323
|
+
# Try editing all 6 VLANs without adding the required device(fails):
|
|
3324
|
+
response = self.client.post(
|
|
3325
|
+
reverse("ipam:vlan_bulk_edit"), data={"pk": [str(vlan.id) for vlan in vlans], "_apply": [""]}
|
|
3326
|
+
)
|
|
3327
|
+
self.assertContains(
|
|
3328
|
+
response,
|
|
3329
|
+
"6 VLANs require a device for the required relationship "VLANs require at least one Device"",
|
|
3330
|
+
)
|
|
3331
|
+
|
|
3332
|
+
# Try editing 3 VLANs without adding the required device(fails):
|
|
3333
|
+
response = self.client.post(
|
|
3334
|
+
reverse("ipam:vlan_bulk_edit"), data={"pk": [str(vlan.id) for vlan in vlans[:3]], "_apply": [""]}
|
|
3335
|
+
)
|
|
3336
|
+
self.assertContains(
|
|
3337
|
+
response,
|
|
3338
|
+
"These VLANs require a device for the required "
|
|
3339
|
+
"relationship "VLANs require at least one Device"",
|
|
3340
|
+
)
|
|
3341
|
+
for vlan in vlans[:3]:
|
|
3342
|
+
self.assertContains(response, str(vlan))
|
|
3343
|
+
|
|
3344
|
+
# Try editing 6 VLANs and adding the required device (succeeds):
|
|
3345
|
+
response = self.client.post(
|
|
3346
|
+
reverse("ipam:vlan_bulk_edit"),
|
|
3347
|
+
data={
|
|
3348
|
+
"pk": [str(vlan.id) for vlan in vlans],
|
|
3349
|
+
"add_cr_vlans_devices_m2m__source": [str(device_for_association.id)],
|
|
3350
|
+
"_apply": [""],
|
|
3351
|
+
},
|
|
3352
|
+
follow=True,
|
|
3353
|
+
)
|
|
3354
|
+
self.assertContains(response, "Updated 6 VLANs")
|
|
3355
|
+
|
|
3356
|
+
# Try editing 6 VLANs and removing the required device (fails):
|
|
3357
|
+
response = self.client.post(
|
|
3358
|
+
reverse("ipam:vlan_bulk_edit"),
|
|
3359
|
+
data={
|
|
3360
|
+
"pk": [str(vlan.id) for vlan in vlans],
|
|
3361
|
+
"remove_cr_vlans_devices_m2m__source": [str(device_for_association.id)],
|
|
3362
|
+
"_apply": [""],
|
|
3363
|
+
},
|
|
3364
|
+
)
|
|
3365
|
+
self.assertContains(
|
|
3366
|
+
response,
|
|
3367
|
+
"6 VLANs require a device for the required relationship "VLANs require at least one Device"",
|
|
3368
|
+
)
|
|
3369
|
+
|
|
3229
3370
|
|
|
3230
3371
|
class RelationshipAssociationTestCase(
|
|
3231
3372
|
# TODO? ViewTestCases.CreateObjectViewTestCase,
|
|
@@ -3608,8 +3749,6 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|
|
3608
3749
|
|
|
3609
3750
|
cls.bulk_edit_data = {
|
|
3610
3751
|
"color": "000000",
|
|
3611
|
-
"description": "I used to be a new role object.",
|
|
3612
|
-
"weight": 255,
|
|
3613
3752
|
}
|
|
3614
3753
|
|
|
3615
3754
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
nautobot/extras/utils.py
CHANGED
|
@@ -579,7 +579,6 @@ def refresh_job_model_from_job_class(job_model_class, job_class, job_queue_class
|
|
|
579
579
|
"installed": True,
|
|
580
580
|
"enabled": False,
|
|
581
581
|
"default_job_queue": default_job_queue,
|
|
582
|
-
"is_singleton": job_class.is_singleton,
|
|
583
582
|
},
|
|
584
583
|
)
|
|
585
584
|
|
|
@@ -666,9 +665,9 @@ def run_kubernetes_job_and_return_job_result(job_queue, job_result, job_kwargs):
|
|
|
666
665
|
"runjob_with_job_result",
|
|
667
666
|
f"{job_result.pk}",
|
|
668
667
|
]
|
|
669
|
-
|
|
668
|
+
logger.info("Creating job pod %s in namespace %s", pod_name, pod_namespace)
|
|
670
669
|
api_instance.create_namespaced_job(body=pod_manifest, namespace=pod_namespace)
|
|
671
|
-
|
|
670
|
+
logger.info("Reading job pod %s in namespace %s", pod_name, pod_namespace)
|
|
672
671
|
api_instance.read_namespaced_job(name="nautobot-job-" + str(job_result.pk), namespace=pod_namespace)
|
|
673
672
|
return job_result
|
|
674
673
|
|