nautobot 2.4.0b1__py3-none-any.whl → 2.4.1__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 +1 -0
- nautobot/circuits/api/views.py +4 -8
- nautobot/circuits/tables.py +2 -1
- nautobot/circuits/templates/circuits/circuit_create.html +1 -7
- nautobot/circuits/views.py +3 -3
- nautobot/cloud/api/views.py +6 -10
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/views.py +0 -16
- nautobot/core/api/constants.py +11 -0
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/filter_backends.py +3 -9
- nautobot/core/api/schema.py +13 -2
- nautobot/core/api/serializers.py +40 -34
- nautobot/core/api/views.py +56 -4
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +2 -2
- nautobot/core/choices.py +2 -2
- nautobot/core/events/__init__.py +3 -3
- nautobot/core/filters.py +67 -35
- nautobot/core/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +14 -11
- nautobot/core/forms/forms.py +33 -2
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +28 -7
- nautobot/core/jobs/bulk_actions.py +285 -0
- nautobot/core/jobs/cleanup.py +48 -12
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/models/__init__.py +3 -1
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +6 -3
- nautobot/core/settings.py +29 -2
- nautobot/core/settings.yaml +21 -0
- nautobot/core/tables.py +79 -61
- nautobot/core/templates/about.html +67 -0
- nautobot/core/templates/inc/media.html +3 -0
- nautobot/core/templates/inc/nav_menu.html +1 -0
- nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
- nautobot/core/templates/inc/tenant_table_row.html +11 -0
- nautobot/core/templates/nautobot_config.py.j2 +13 -0
- nautobot/core/templates/search.html +7 -0
- 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 +7 -0
- nautobot/core/templatetags/helpers.py +11 -2
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +170 -15
- nautobot/core/testing/filters.py +45 -10
- nautobot/core/testing/forms.py +2 -0
- nautobot/core/testing/integration.py +86 -4
- nautobot/core/testing/mixins.py +7 -2
- nautobot/core/testing/views.py +44 -29
- nautobot/core/tests/integration/test_app_home.py +0 -1
- nautobot/core/tests/integration/test_app_navbar.py +0 -1
- nautobot/core/tests/integration/test_filters.py +0 -2
- nautobot/core/tests/integration/test_home.py +0 -1
- nautobot/core/tests/integration/test_navbar.py +0 -1
- nautobot/core/tests/integration/test_view_authentication.py +1 -0
- nautobot/core/tests/runner.py +1 -1
- nautobot/core/tests/test_api.py +98 -1
- nautobot/core/tests/test_csv.py +25 -3
- nautobot/core/tests/test_filters.py +209 -246
- nautobot/core/tests/test_forms.py +1 -0
- nautobot/core/tests/test_jobs.py +460 -1
- nautobot/core/tests/test_models.py +9 -0
- nautobot/core/tests/test_settings_schema.py +7 -0
- nautobot/core/tests/test_tables.py +100 -0
- nautobot/core/tests/test_utils.py +63 -1
- nautobot/core/tests/test_views.py +30 -3
- nautobot/core/ui/nav.py +1 -0
- nautobot/core/ui/object_detail.py +15 -1
- nautobot/core/urls.py +11 -0
- nautobot/core/utils/lookup.py +11 -8
- nautobot/core/utils/querysets.py +64 -0
- nautobot/core/utils/requests.py +24 -9
- nautobot/core/views/__init__.py +42 -0
- nautobot/core/views/generic.py +131 -197
- nautobot/core/views/mixins.py +126 -38
- nautobot/core/views/renderers.py +6 -6
- nautobot/dcim/api/serializers.py +56 -64
- nautobot/dcim/api/views.py +47 -113
- nautobot/dcim/constants.py +6 -13
- nautobot/dcim/factory.py +6 -1
- nautobot/dcim/filters/__init__.py +31 -2
- nautobot/dcim/forms.py +36 -17
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
- nautobot/dcim/models/__init__.py +1 -1
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +22 -20
- nautobot/dcim/models/devices.py +10 -1
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +6 -5
- nautobot/dcim/models/racks.py +4 -4
- nautobot/dcim/tables/__init__.py +3 -3
- nautobot/dcim/tables/devices.py +7 -5
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller_create.html +1 -7
- nautobot/dcim/templates/dcim/controller_retrieve.html +1 -9
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +2 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +5 -0
- nautobot/dcim/templates/dcim/device.html +1 -9
- nautobot/dcim/templates/dcim/device_edit.html +36 -37
- nautobot/dcim/templates/dcim/location.html +1 -9
- nautobot/dcim/templates/dcim/location_edit.html +1 -7
- nautobot/dcim/templates/dcim/rack.html +1 -9
- nautobot/dcim/templates/dcim/rack_edit.html +1 -7
- nautobot/dcim/templates/dcim/rackreservation.html +1 -9
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -9
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +1 -7
- nautobot/dcim/tests/integration/test_controller.py +62 -0
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
- nautobot/dcim/tests/integration/test_device_bulk_delete.py +189 -0
- nautobot/dcim/tests/integration/test_device_bulk_edit.py +181 -0
- nautobot/dcim/tests/test_api.py +16 -5
- nautobot/dcim/tests/test_filters.py +33 -0
- nautobot/dcim/tests/test_forms.py +51 -2
- nautobot/dcim/tests/test_graphql.py +52 -0
- nautobot/dcim/tests/test_jobs.py +118 -0
- nautobot/dcim/tests/test_models.py +52 -9
- nautobot/dcim/tests/test_views.py +21 -83
- nautobot/dcim/views.py +1 -13
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +90 -85
- nautobot/extras/api/views.py +22 -27
- nautobot/extras/constants.py +2 -0
- nautobot/extras/filters/__init__.py +8 -6
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +139 -31
- nautobot/extras/forms/mixins.py +14 -6
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +1 -2
- nautobot/extras/jobs.py +85 -18
- nautobot/extras/managers.py +3 -1
- nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
- nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
- nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
- nautobot/extras/models/__init__.py +1 -1
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/customfields.py +12 -11
- nautobot/extras/models/groups.py +11 -9
- nautobot/extras/models/jobs.py +23 -4
- nautobot/extras/models/models.py +2 -2
- nautobot/extras/plugins/__init__.py +13 -2
- nautobot/extras/plugins/marketplace_manifest.yml +84 -79
- nautobot/extras/plugins/tables.py +16 -14
- nautobot/extras/plugins/views.py +65 -69
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/tables.py +7 -5
- nautobot/extras/templates/extras/dynamicgroup.html +1 -9
- nautobot/extras/templates/extras/job_detail.html +16 -0
- nautobot/extras/templates/extras/job_edit.html +1 -0
- nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
- nautobot/extras/templates/extras/marketplace.html +29 -11
- nautobot/extras/templates/extras/plugin_detail.html +32 -15
- nautobot/extras/templates/extras/plugins_tiles.html +21 -10
- nautobot/extras/templatetags/job_buttons.py +4 -4
- 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 +3 -1
- 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/singleton.py +16 -0
- 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 +0 -2
- nautobot/extras/tests/test_api.py +13 -13
- nautobot/extras/tests/test_customfields.py +1 -1
- nautobot/extras/tests/test_datasources.py +2 -1
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_filters.py +6 -6
- nautobot/extras/tests/test_forms.py +33 -1
- nautobot/extras/tests/test_jobs.py +178 -32
- nautobot/extras/tests/test_models.py +16 -10
- nautobot/extras/tests/test_plugins.py +62 -9
- nautobot/extras/tests/test_relationships.py +120 -9
- nautobot/extras/tests/test_views.py +56 -194
- nautobot/extras/utils.py +3 -2
- nautobot/extras/views.py +30 -98
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +41 -33
- nautobot/ipam/api/views.py +68 -117
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +3 -2
- nautobot/ipam/lookups.py +101 -62
- nautobot/ipam/models.py +66 -16
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +23 -7
- nautobot/ipam/templates/ipam/ipaddress.html +1 -9
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
- nautobot/ipam/templates/ipam/prefix.html +1 -9
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
- nautobot/ipam/templates/ipam/vlan.html +1 -9
- nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
- nautobot/ipam/tests/test_api.py +436 -3
- nautobot/ipam/tests/test_forms.py +49 -47
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +95 -34
- nautobot/ipam/tests/test_querysets.py +63 -1
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/ipam/utils/__init__.py +36 -6
- nautobot/ipam/views.py +61 -87
- 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 +40 -2
- 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 +46 -4
- nautobot/project-static/docs/apps/index.html +46 -4
- nautobot/project-static/docs/apps/nautobot-apps.html +47 -6
- nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +62 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +59 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +374 -122
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +90 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +95 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +53 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +52 -5
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +79 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +102 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +108 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +131 -38
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +239 -65
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +581 -165
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +109 -36
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +453 -167
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +493 -211
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +60 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +71 -15
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +407 -55
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +620 -205
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +858 -412
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +59 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +448 -186
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +365 -147
- nautobot/project-static/docs/development/apps/api/configuration-view.html +46 -4
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/global-search.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/graphql.html +46 -4
- nautobot/project-static/docs/development/apps/api/models/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +46 -4
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +68 -7
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +46 -4
- nautobot/project-static/docs/development/apps/api/prometheus.html +46 -4
- nautobot/project-static/docs/development/apps/api/setup.html +46 -4
- nautobot/project-static/docs/development/apps/api/testing.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +46 -4
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/base-template.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/index.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/notes.html +46 -4
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +52 -6
- nautobot/project-static/docs/development/apps/api/views/urls.html +46 -4
- nautobot/project-static/docs/development/apps/index.html +46 -4
- nautobot/project-static/docs/development/apps/migration/code-updates.html +46 -4
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +46 -4
- nautobot/project-static/docs/development/apps/migration/from-v1.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +46 -4
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +46 -4
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +50 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +46 -4
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +211 -14
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +46 -4
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +46 -4
- nautobot/project-static/docs/development/core/application-registry.html +46 -4
- nautobot/project-static/docs/development/core/best-practices.html +46 -4
- nautobot/project-static/docs/development/core/bootstrap-ui.html +46 -4
- nautobot/project-static/docs/development/core/caching.html +46 -4
- nautobot/project-static/docs/development/core/controllers.html +46 -4
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +73 -74
- nautobot/project-static/docs/development/core/generic-views.html +46 -4
- nautobot/project-static/docs/development/core/getting-started.html +249 -224
- nautobot/project-static/docs/development/core/homepage.html +49 -7
- nautobot/project-static/docs/development/core/index.html +46 -4
- nautobot/project-static/docs/development/core/{local-k8s.html → minikube-dev-environment-for-k8s-jobs.html} +469 -168
- nautobot/project-static/docs/development/core/model-checklist.html +56 -12
- nautobot/project-static/docs/development/core/model-features.html +46 -4
- nautobot/project-static/docs/development/core/natural-keys.html +46 -4
- nautobot/project-static/docs/development/core/navigation-menu.html +46 -4
- nautobot/project-static/docs/development/core/release-checklist.html +49 -7
- nautobot/project-static/docs/development/core/role-internals.html +46 -4
- nautobot/project-static/docs/development/core/settings.html +46 -4
- nautobot/project-static/docs/development/core/style-guide.html +49 -7
- nautobot/project-static/docs/development/core/templates.html +46 -4
- nautobot/project-static/docs/development/core/testing.html +46 -4
- nautobot/project-static/docs/development/core/ui-component-framework.html +369 -273
- nautobot/project-static/docs/development/core/user-preferences.html +46 -4
- nautobot/project-static/docs/development/index.html +46 -4
- nautobot/project-static/docs/development/jobs/index.html +216 -122
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +46 -4
- nautobot/project-static/docs/index.html +54 -23
- 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/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +47 -7
- nautobot/project-static/docs/overview/design_philosophy.html +46 -4
- nautobot/project-static/docs/release-notes/index.html +52 -12
- nautobot/project-static/docs/release-notes/version-1.0.html +234 -193
- nautobot/project-static/docs/release-notes/version-1.1.html +231 -190
- nautobot/project-static/docs/release-notes/version-1.2.html +306 -265
- nautobot/project-static/docs/release-notes/version-1.3.html +332 -291
- nautobot/project-static/docs/release-notes/version-1.4.html +417 -377
- nautobot/project-static/docs/release-notes/version-1.5.html +605 -566
- nautobot/project-static/docs/release-notes/version-1.6.html +904 -447
- nautobot/project-static/docs/release-notes/version-2.0.html +528 -489
- nautobot/project-static/docs/release-notes/version-2.1.html +363 -324
- nautobot/project-static/docs/release-notes/version-2.2.html +356 -317
- nautobot/project-static/docs/release-notes/version-2.3.html +997 -352
- nautobot/project-static/docs/release-notes/version-2.4.html +525 -101
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +295 -287
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +48 -6
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +46 -4
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +110 -8
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +46 -4
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +48 -6
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +66 -8
- nautobot/project-static/docs/user-guide/administration/installation/index.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +47 -5
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +46 -4
- nautobot/project-static/docs/user-guide/administration/installation/services.html +46 -4
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +46 -4
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +46 -4
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +46 -4
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +49 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +46 -4
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +50 -12
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +49 -7
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +46 -4
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +51 -7
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +46 -4
- 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/ip-address-merge-tool.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +46 -4
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +49 -7
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
- nautobot/project-static/docs/user-guide/index.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +50 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +50 -7
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +49 -7
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +47 -5
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +94 -25
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +74 -5
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +46 -4
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +46 -4
- nautobot/project-static/js/forms.js +1 -1
- nautobot/tenancy/api/views.py +9 -13
- nautobot/tenancy/views.py +4 -2
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +5 -4
- nautobot/users/api/views.py +3 -3
- nautobot/virtualization/api/serializers.py +4 -4
- nautobot/virtualization/api/views.py +5 -24
- nautobot/virtualization/filters.py +20 -3
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
- nautobot/virtualization/tests/test_filters.py +17 -0
- nautobot/wireless/filters.py +2 -2
- nautobot/wireless/forms.py +1 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -9
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
- nautobot/wireless/tests/test_filters.py +29 -1
- nautobot/wireless/tests/test_views.py +22 -1
- nautobot/wireless/views.py +0 -10
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/METADATA +6 -6
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/RECORD +600 -550
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/WHEEL +1 -1
- nautobot/core/fixtures/user-data.json +0 -59
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.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 TestCase
|
|
15
|
+
from nautobot.core.testing import create_job_result_and_run_job, TestCase, TransactionTestCase
|
|
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,9 +29,22 @@ 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
|
-
|
|
32
|
+
from nautobot.extras.choices import (
|
|
33
|
+
JobResultStatusChoices,
|
|
34
|
+
LogLevelChoices,
|
|
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
|
|
35
48
|
|
|
36
49
|
|
|
37
50
|
class RelationshipBaseTest:
|
|
@@ -354,20 +367,24 @@ class RelationshipTest(RelationshipBaseTest, ModelTestCases.BaseModelTestCase):
|
|
|
354
367
|
self.assertFalse(field.required)
|
|
355
368
|
self.assertIsInstance(field, DynamicModelMultipleChoiceField)
|
|
356
369
|
self.assertEqual(field.label, "My VLANs")
|
|
357
|
-
self.assertEqual(field.query_params, {})
|
|
370
|
+
self.assertEqual(field.query_params, {"exclude_m2m": "true"})
|
|
358
371
|
|
|
359
372
|
field = self.m2m_1.to_form_field("destination")
|
|
360
373
|
self.assertFalse(field.required)
|
|
361
374
|
self.assertIsInstance(field, DynamicModelMultipleChoiceField)
|
|
362
375
|
self.assertEqual(field.label, "My Racks")
|
|
363
376
|
self.assertEqual(
|
|
364
|
-
field.query_params,
|
|
377
|
+
field.query_params,
|
|
378
|
+
{
|
|
379
|
+
"location": [self.locations[0].name, self.locations[1].name, self.locations[2].name],
|
|
380
|
+
"exclude_m2m": "true",
|
|
381
|
+
},
|
|
365
382
|
)
|
|
366
383
|
|
|
367
384
|
field = self.m2ms_1.to_form_field("peer")
|
|
368
385
|
self.assertFalse(field.required)
|
|
369
386
|
self.assertIsInstance(field, DynamicModelMultipleChoiceField)
|
|
370
|
-
self.assertEqual(field.query_params, {})
|
|
387
|
+
self.assertEqual(field.query_params, {"exclude_m2m": "true"})
|
|
371
388
|
|
|
372
389
|
def test_to_form_field_o2m(self):
|
|
373
390
|
field = self.o2m_1.to_form_field("source")
|
|
@@ -1326,7 +1343,7 @@ class RelationshipTableTest(RelationshipBaseTest, TestCase):
|
|
|
1326
1343
|
self.assertIsNotNone(relationship_column)
|
|
1327
1344
|
self.assertIsInstance(relationship_column, RelationshipColumn)
|
|
1328
1345
|
|
|
1329
|
-
rendered_value = bound_row.get_cell(internal_col_name)
|
|
1346
|
+
rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
|
|
1330
1347
|
# Test if the expected value is in the rendered value.
|
|
1331
1348
|
# Exact match is difficult because the order of rendering is unpredictable.
|
|
1332
1349
|
for value in col_expected_value:
|
|
@@ -1432,7 +1449,7 @@ class RequiredRelationshipTestMixin:
|
|
|
1432
1449
|
required_on="source",
|
|
1433
1450
|
)
|
|
1434
1451
|
relationship_o2o.validated_save()
|
|
1435
|
-
vlan_group = VLANGroup.objects.
|
|
1452
|
+
vlan_group = VLANGroup.objects.create(name="Test VLANGroup 1")
|
|
1436
1453
|
|
|
1437
1454
|
tests_params = [
|
|
1438
1455
|
# Required many-to-many:
|
|
@@ -1712,3 +1729,97 @@ class RequiredRelationshipTestMixin:
|
|
|
1712
1729
|
}
|
|
1713
1730
|
}
|
|
1714
1731
|
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,5 +1,4 @@
|
|
|
1
1
|
from datetime import timedelta
|
|
2
|
-
from http import HTTPStatus
|
|
3
2
|
import json
|
|
4
3
|
from unittest import mock
|
|
5
4
|
import urllib.parse
|
|
@@ -26,7 +25,6 @@ from nautobot.core.testing.utils import disable_warnings, get_deletable_objects,
|
|
|
26
25
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
27
26
|
from nautobot.dcim.models import (
|
|
28
27
|
ConsolePort,
|
|
29
|
-
Controller,
|
|
30
28
|
Device,
|
|
31
29
|
DeviceType,
|
|
32
30
|
Interface,
|
|
@@ -34,7 +32,6 @@ from nautobot.dcim.models import (
|
|
|
34
32
|
LocationType,
|
|
35
33
|
Manufacturer,
|
|
36
34
|
)
|
|
37
|
-
from nautobot.dcim.tests import test_views
|
|
38
35
|
from nautobot.extras.choices import (
|
|
39
36
|
CustomFieldTypeChoices,
|
|
40
37
|
DynamicGroupTypeChoices,
|
|
@@ -89,10 +86,9 @@ from nautobot.extras.templatetags.job_buttons import NO_CONFIRM_BUTTON
|
|
|
89
86
|
from nautobot.extras.tests.constants import BIG_GRAPHQL_DEVICE_QUERY
|
|
90
87
|
from nautobot.extras.tests.test_relationships import RequiredRelationshipTestMixin
|
|
91
88
|
from nautobot.extras.utils import RoleModelsQuery, TaggableClassesQuery
|
|
92
|
-
from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
|
|
89
|
+
from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup, VRF
|
|
93
90
|
from nautobot.tenancy.models import Tenant
|
|
94
91
|
from nautobot.users.models import ObjectPermission
|
|
95
|
-
from nautobot.wireless.models import ControllerManagedDeviceGroupWirelessNetworkAssignment
|
|
96
92
|
|
|
97
93
|
# Use the proper swappable User model
|
|
98
94
|
User = get_user_model()
|
|
@@ -915,6 +911,30 @@ class DynamicGroupTestCase(
|
|
|
915
911
|
self.form_data["content_type"] = instance.content_type.pk # Content-type is not editable after creation
|
|
916
912
|
super().test_edit_object_with_constrained_permission()
|
|
917
913
|
|
|
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
|
+
|
|
918
938
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
919
939
|
def test_edit_saved_filter(self):
|
|
920
940
|
"""Test that editing a filter works using the edit view."""
|
|
@@ -1698,21 +1718,21 @@ class ScheduledJobTestCase(
|
|
|
1698
1718
|
user = User.objects.create(username="user1", is_active=True)
|
|
1699
1719
|
ScheduledJob.objects.create(
|
|
1700
1720
|
name="test1",
|
|
1701
|
-
task="pass.
|
|
1721
|
+
task="pass.TestPassJob",
|
|
1702
1722
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1703
1723
|
user=user,
|
|
1704
1724
|
start_time=timezone.now(),
|
|
1705
1725
|
)
|
|
1706
1726
|
ScheduledJob.objects.create(
|
|
1707
1727
|
name="test2",
|
|
1708
|
-
task="pass.
|
|
1728
|
+
task="pass.TestPassJob",
|
|
1709
1729
|
interval=JobExecutionType.TYPE_DAILY,
|
|
1710
1730
|
user=user,
|
|
1711
1731
|
start_time=timezone.now(),
|
|
1712
1732
|
)
|
|
1713
1733
|
ScheduledJob.objects.create(
|
|
1714
1734
|
name="test3",
|
|
1715
|
-
task="pass.
|
|
1735
|
+
task="pass.TestPassJob",
|
|
1716
1736
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1717
1737
|
user=user,
|
|
1718
1738
|
start_time=timezone.now(),
|
|
@@ -1726,7 +1746,7 @@ class ScheduledJobTestCase(
|
|
|
1726
1746
|
ScheduledJob.objects.create(
|
|
1727
1747
|
enabled=False,
|
|
1728
1748
|
name="test4",
|
|
1729
|
-
task="pass.
|
|
1749
|
+
task="pass.TestPassJob",
|
|
1730
1750
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1731
1751
|
user=self.user,
|
|
1732
1752
|
start_time=timezone.now(),
|
|
@@ -1743,7 +1763,7 @@ class ScheduledJobTestCase(
|
|
|
1743
1763
|
ScheduledJob.objects.create(
|
|
1744
1764
|
enabled=True,
|
|
1745
1765
|
name=name,
|
|
1746
|
-
task="pass.
|
|
1766
|
+
task="pass.TestPassJob",
|
|
1747
1767
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1748
1768
|
user=self.user,
|
|
1749
1769
|
start_time=timezone.now(),
|
|
@@ -1774,7 +1794,7 @@ class ScheduledJobTestCase(
|
|
|
1774
1794
|
ScheduledJob.objects.create(
|
|
1775
1795
|
enabled=True,
|
|
1776
1796
|
name="test11",
|
|
1777
|
-
task="pass.
|
|
1797
|
+
task="pass.TestPassJob",
|
|
1778
1798
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1779
1799
|
user=self.user,
|
|
1780
1800
|
start_time=timezone.now(),
|
|
@@ -1808,7 +1828,7 @@ class ApprovalQueueTestCase(
|
|
|
1808
1828
|
def setUp(self):
|
|
1809
1829
|
super().setUp()
|
|
1810
1830
|
self.job_model = Job.objects.get_for_class_path("dry_run.TestDryRun")
|
|
1811
|
-
self.job_model_2 = Job.objects.get_for_class_path("fail.
|
|
1831
|
+
self.job_model_2 = Job.objects.get_for_class_path("fail.TestFailJob")
|
|
1812
1832
|
|
|
1813
1833
|
ScheduledJob.objects.create(
|
|
1814
1834
|
name="test1",
|
|
@@ -1821,7 +1841,7 @@ class ApprovalQueueTestCase(
|
|
|
1821
1841
|
)
|
|
1822
1842
|
ScheduledJob.objects.create(
|
|
1823
1843
|
name="test2",
|
|
1824
|
-
task="fail.
|
|
1844
|
+
task="fail.TestFailJob",
|
|
1825
1845
|
job_model=self.job_model_2,
|
|
1826
1846
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1827
1847
|
user=self.user,
|
|
@@ -1834,7 +1854,7 @@ class ApprovalQueueTestCase(
|
|
|
1834
1854
|
|
|
1835
1855
|
ScheduledJob.objects.create(
|
|
1836
1856
|
name="test4",
|
|
1837
|
-
task="pass.
|
|
1857
|
+
task="pass.TestPassJob",
|
|
1838
1858
|
job_model=self.job_model,
|
|
1839
1859
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1840
1860
|
user=self.user,
|
|
@@ -2225,8 +2245,8 @@ class JobResultTestCase(
|
|
|
2225
2245
|
|
|
2226
2246
|
@classmethod
|
|
2227
2247
|
def setUpTestData(cls):
|
|
2228
|
-
JobResult.objects.create(name="pass.
|
|
2229
|
-
JobResult.objects.create(name="fail.
|
|
2248
|
+
JobResult.objects.create(name="pass.TestPassJob")
|
|
2249
|
+
JobResult.objects.create(name="fail.TestFailJob")
|
|
2230
2250
|
JobLogEntry.objects.create(
|
|
2231
2251
|
log_level=LogLevelChoices.LOG_INFO,
|
|
2232
2252
|
job_result=JobResult.objects.first(),
|
|
@@ -2280,7 +2300,7 @@ class JobTestCase(
|
|
|
2280
2300
|
# Job model objects are automatically created during database migrations
|
|
2281
2301
|
|
|
2282
2302
|
# But we do need to make sure the ones we're testing are flagged appropriately
|
|
2283
|
-
cls.test_pass = Job.objects.get(job_class_name="
|
|
2303
|
+
cls.test_pass = Job.objects.get(job_class_name="TestPassJob")
|
|
2284
2304
|
default_job_queue = JobQueue.objects.get(name="default", queue_type=JobQueueTypeChoices.TYPE_CELERY)
|
|
2285
2305
|
cls.test_pass.default_job_queue = default_job_queue
|
|
2286
2306
|
cls.test_pass.enabled = True
|
|
@@ -2416,35 +2436,6 @@ class JobTestCase(
|
|
|
2416
2436
|
# assert Job still exists
|
|
2417
2437
|
self.assertTrue(self._get_queryset().filter(name=job_name).exists())
|
|
2418
2438
|
|
|
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
|
-
|
|
2448
2439
|
def validate_job_data_after_bulk_edit(self, pk_list, old_data):
|
|
2449
2440
|
# Name is bulk-editable
|
|
2450
2441
|
overridable_fields = [field for field in JOB_OVERRIDABLE_FIELDS if field != "name"]
|
|
@@ -2513,7 +2504,7 @@ class JobTestCase(
|
|
|
2513
2504
|
self.add_permissions("extras.run_job")
|
|
2514
2505
|
for run_url in self.run_urls:
|
|
2515
2506
|
response = self.client.get(run_url)
|
|
2516
|
-
self.assertBodyContains(response, "
|
|
2507
|
+
self.assertBodyContains(response, "TestPassJob")
|
|
2517
2508
|
|
|
2518
2509
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
|
|
2519
2510
|
def test_get_run_with_constrained_permission(self):
|
|
@@ -2612,12 +2603,12 @@ class JobTestCase(
|
|
|
2612
2603
|
self.add_permissions("extras.run_job")
|
|
2613
2604
|
|
|
2614
2605
|
for run_url in (
|
|
2615
|
-
reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.
|
|
2616
|
-
reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="
|
|
2606
|
+
reverse("extras:job_run_by_class_path", kwargs={"class_path": "fail.TestFailJob"}),
|
|
2607
|
+
reverse("extras:job_run", kwargs={"pk": Job.objects.get(job_class_name="TestFailJob").pk}),
|
|
2617
2608
|
):
|
|
2618
2609
|
response = self.client.post(run_url, self.data_run_immediately)
|
|
2619
2610
|
self.assertBodyContains(response, "Job is not enabled to be run")
|
|
2620
|
-
self.assertFalse(JobResult.objects.filter(name="fail.
|
|
2611
|
+
self.assertFalse(JobResult.objects.filter(name="fail.TestFailJob").exists())
|
|
2621
2612
|
|
|
2622
2613
|
def test_run_now_missing_args(self):
|
|
2623
2614
|
self.add_permissions("extras.run_job")
|
|
@@ -2653,10 +2644,16 @@ class JobTestCase(
|
|
|
2653
2644
|
job_celery_kwargs = {
|
|
2654
2645
|
"nautobot_job_job_model_id": self.test_required_args.id,
|
|
2655
2646
|
"nautobot_job_profile": True,
|
|
2647
|
+
"nautobot_job_ignore_singleton_lock": True,
|
|
2656
2648
|
"nautobot_job_user_id": self.user.id,
|
|
2657
2649
|
"queue": job_queue.name,
|
|
2658
2650
|
}
|
|
2659
2651
|
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()
|
|
2660
2657
|
previous_result = JobResult.objects.create(
|
|
2661
2658
|
job_model=self.test_required_args,
|
|
2662
2659
|
user=self.user,
|
|
@@ -2673,6 +2670,9 @@ class JobTestCase(
|
|
|
2673
2670
|
content,
|
|
2674
2671
|
)
|
|
2675
2672
|
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,59 +2844,6 @@ 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
|
-
|
|
2900
2847
|
|
|
2901
2848
|
class JobButtonTestCase(
|
|
2902
2849
|
ViewTestCases.CreateObjectViewTestCase,
|
|
@@ -3008,8 +2955,9 @@ class JobButtonRenderingTestCase(TestCase):
|
|
|
3008
2955
|
response = self.client.get(self.location_type.get_absolute_url(), follow=True)
|
|
3009
2956
|
self.assertEqual(response.status_code, 200)
|
|
3010
2957
|
content = extract_page_body(response.content.decode(response.charset))
|
|
3011
|
-
job_queues = self.job.job_queues.all()
|
|
3012
|
-
|
|
2958
|
+
job_queues = self.job.job_queues.all()
|
|
2959
|
+
_job_queue = job_queues[0]
|
|
2960
|
+
self.assertIn(f'<input type="hidden" name="_job_queue" value="{_job_queue.pk}">', content, content)
|
|
3013
2961
|
|
|
3014
2962
|
self.job.job_queues_override = False
|
|
3015
2963
|
self.job.save()
|
|
@@ -3018,7 +2966,7 @@ class JobButtonRenderingTestCase(TestCase):
|
|
|
3018
2966
|
self.assertEqual(response.status_code, 200)
|
|
3019
2967
|
content = extract_page_body(response.content.decode(response.charset))
|
|
3020
2968
|
self.assertIn(
|
|
3021
|
-
f'<input type="hidden" name="_job_queue" value="{self.job.default_job_queue.
|
|
2969
|
+
f'<input type="hidden" name="_job_queue" value="{self.job.default_job_queue.pk}">', content, content
|
|
3022
2970
|
)
|
|
3023
2971
|
|
|
3024
2972
|
def test_view_object_with_unsafe_text(self):
|
|
@@ -3279,94 +3227,6 @@ class RelationshipTestCase(
|
|
|
3279
3227
|
|
|
3280
3228
|
cls.slug_test_object = "Primary Interface"
|
|
3281
3229
|
|
|
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
|
-
|
|
3370
3230
|
|
|
3371
3231
|
class RelationshipAssociationTestCase(
|
|
3372
3232
|
# TODO? ViewTestCases.CreateObjectViewTestCase,
|
|
@@ -3749,6 +3609,8 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|
|
3749
3609
|
|
|
3750
3610
|
cls.bulk_edit_data = {
|
|
3751
3611
|
"color": "000000",
|
|
3612
|
+
"description": "I used to be a new role object.",
|
|
3613
|
+
"weight": 255,
|
|
3752
3614
|
}
|
|
3753
3615
|
|
|
3754
3616
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
nautobot/extras/utils.py
CHANGED
|
@@ -579,6 +579,7 @@ 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,
|
|
582
583
|
},
|
|
583
584
|
)
|
|
584
585
|
|
|
@@ -665,9 +666,9 @@ def run_kubernetes_job_and_return_job_result(job_queue, job_result, job_kwargs):
|
|
|
665
666
|
"runjob_with_job_result",
|
|
666
667
|
f"{job_result.pk}",
|
|
667
668
|
]
|
|
668
|
-
|
|
669
|
+
job_result.log(f"Creating job pod {pod_name} in namespace {pod_namespace}")
|
|
669
670
|
api_instance.create_namespaced_job(body=pod_manifest, namespace=pod_namespace)
|
|
670
|
-
|
|
671
|
+
job_result.log(f"Reading job pod {pod_name} in namespace {pod_namespace}")
|
|
671
672
|
api_instance.read_namespaced_job(name="nautobot-job-" + str(job_result.pk), namespace=pod_namespace)
|
|
672
673
|
return job_result
|
|
673
674
|
|