nautobot 2.4.0b1__py3-none-any.whl → 2.4.2__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/tests/integration/test_circuits_bulk_operations.py +43 -0
- nautobot/circuits/tests/integration/test_relationships.py +1 -1
- 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/apps/__init__.py +0 -5
- 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/generic/object_bulk_destroy.html +1 -1
- 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 +514 -5
- 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 +492 -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/git.py +7 -2
- 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 +136 -41
- nautobot/core/views/renderers.py +6 -6
- nautobot/core/views/utils.py +2 -2
- 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 +48 -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 +9 -6
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/cable.html +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/base.html +1 -1
- nautobot/dcim/templates/dcim/device.html +3 -11
- nautobot/dcim/templates/dcim/device_component.html +1 -1
- nautobot/dcim/templates/dcim/device_edit.html +36 -37
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/location.html +2 -10
- nautobot/dcim/templates/dcim/location_edit.html +1 -7
- nautobot/dcim/templates/dcim/locationtype.html +1 -1
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/manufacturer.html +1 -1
- nautobot/dcim/templates/dcim/platform.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/rack.html +2 -10
- nautobot/dcim/templates/dcim/rack_edit.html +1 -7
- nautobot/dcim/templates/dcim/rackgroup.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation.html +3 -11
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- 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_operations.py +30 -0
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -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 +30 -84
- nautobot/dcim/views.py +13 -28
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +123 -85
- nautobot/extras/api/views.py +33 -30
- nautobot/extras/constants.py +3 -0
- nautobot/extras/datasources/git.py +125 -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/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
- nautobot/extras/models/__init__.py +1 -1
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/customfields.py +41 -23
- nautobot/extras/models/datasources.py +85 -0
- nautobot/extras/models/groups.py +11 -9
- nautobot/extras/models/jobs.py +23 -4
- nautobot/extras/models/models.py +17 -2
- nautobot/extras/models/relationships.py +17 -5
- 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/signals.py +15 -1
- nautobot/extras/tables.py +7 -5
- nautobot/extras/templates/extras/computedfield.html +1 -1
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/customfield.html +1 -1
- nautobot/extras/templates/extras/customlink.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup.html +2 -10
- nautobot/extras/templates/extras/exporttemplate.html +1 -1
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/graphqlquery.html +1 -1
- nautobot/extras/templates/extras/job_detail.html +17 -1
- nautobot/extras/templates/extras/job_edit.html +1 -0
- nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
- nautobot/extras/templates/extras/jobhook.html +1 -1
- nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/marketplace.html +29 -11
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/plugin_detail.html +33 -16
- nautobot/extras/templates/extras/plugins_tiles.html +21 -10
- nautobot/extras/templates/extras/relationship.html +1 -63
- nautobot/extras/templates/extras/role_retrieve.html +1 -1
- nautobot/extras/templates/extras/scheduledjob.html +1 -1
- nautobot/extras/templates/extras/secret.html +1 -1
- nautobot/extras/templates/extras/secretsgroup.html +1 -1
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/webhook.html +1 -1
- 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/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
- nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
- nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
- nautobot/extras/tests/git_helper.py +9 -1
- nautobot/extras/tests/integration/__init__.py +29 -16
- nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
- nautobot/extras/tests/test_api.py +19 -13
- nautobot/extras/tests/test_customfields.py +50 -52
- nautobot/extras/tests/test_datasources.py +29 -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 +299 -10
- nautobot/extras/tests/test_plugins.py +62 -9
- nautobot/extras/tests/test_relationships.py +120 -9
- nautobot/extras/tests/test_utils.py +22 -1
- nautobot/extras/tests/test_views.py +56 -194
- nautobot/extras/utils.py +20 -10
- nautobot/extras/views.py +85 -110
- 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 +74 -18
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +25 -9
- nautobot/ipam/templates/ipam/ipaddress.html +2 -10
- 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 +2 -10
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
- nautobot/ipam/templates/ipam/rir.html +1 -1
- nautobot/ipam/templates/ipam/routetarget.html +1 -1
- nautobot/ipam/templates/ipam/service.html +1 -1
- nautobot/ipam/templates/ipam/vlan.html +2 -10
- nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
- nautobot/ipam/templates/ipam/vlangroup.html +1 -1
- nautobot/ipam/templates/ipam/vrf.html +1 -1
- 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 +119 -34
- nautobot/ipam/tests/test_querysets.py +63 -1
- nautobot/ipam/tests/test_utils.py +41 -2
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/ipam/utils/__init__.py +54 -17
- 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 +131 -14
- nautobot/project-static/docs/apps/index.html +131 -14
- nautobot/project-static/docs/apps/nautobot-apps.html +132 -16
- nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.60a45f97.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.60a45f97.min.js.map} +2 -2
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
- nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +147 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +144 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +459 -132
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +175 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +180 -31
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +138 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +137 -15
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +164 -27
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +187 -38
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +193 -31
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +216 -48
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +324 -75
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +666 -175
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +194 -46
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +538 -177
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +578 -221
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +145 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +156 -25
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +492 -65
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +705 -215
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +943 -422
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +144 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +619 -200
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +474 -159
- nautobot/project-static/docs/development/apps/api/configuration-view.html +131 -14
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +131 -14
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +131 -14
- nautobot/project-static/docs/development/apps/api/models/global-search.html +131 -14
- nautobot/project-static/docs/development/apps/api/models/graphql.html +131 -14
- nautobot/project-static/docs/development/apps/api/models/index.html +131 -14
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +131 -14
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +153 -17
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +131 -14
- nautobot/project-static/docs/development/apps/api/prometheus.html +131 -14
- nautobot/project-static/docs/development/apps/api/setup.html +131 -14
- nautobot/project-static/docs/development/apps/api/testing.html +131 -14
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +131 -14
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +131 -14
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +131 -14
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +131 -14
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/base-template.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/index.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/notes.html +131 -14
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +137 -16
- nautobot/project-static/docs/development/apps/api/views/urls.html +131 -14
- nautobot/project-static/docs/development/apps/index.html +131 -14
- nautobot/project-static/docs/development/apps/migration/code-updates.html +131 -14
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +131 -14
- nautobot/project-static/docs/development/apps/migration/from-v1.html +131 -14
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +131 -14
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +131 -14
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +131 -14
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +131 -14
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +135 -18
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +131 -14
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +297 -25
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +131 -14
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +131 -14
- nautobot/project-static/docs/development/core/application-registry.html +131 -14
- nautobot/project-static/docs/development/core/best-practices.html +131 -14
- nautobot/project-static/docs/development/core/bootstrap-ui.html +131 -14
- nautobot/project-static/docs/development/core/caching.html +131 -14
- nautobot/project-static/docs/development/core/controllers.html +131 -14
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +158 -84
- nautobot/project-static/docs/development/core/generic-views.html +131 -14
- nautobot/project-static/docs/development/core/getting-started.html +334 -234
- nautobot/project-static/docs/development/core/homepage.html +134 -17
- nautobot/project-static/docs/development/core/index.html +131 -14
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9829 -0
- nautobot/project-static/docs/development/core/model-checklist.html +141 -22
- nautobot/project-static/docs/development/core/model-features.html +131 -14
- nautobot/project-static/docs/development/core/natural-keys.html +131 -14
- nautobot/project-static/docs/development/core/navigation-menu.html +131 -14
- nautobot/project-static/docs/development/core/release-checklist.html +134 -17
- nautobot/project-static/docs/development/core/role-internals.html +131 -14
- nautobot/project-static/docs/development/core/settings.html +131 -14
- nautobot/project-static/docs/development/core/style-guide.html +134 -17
- nautobot/project-static/docs/development/core/templates.html +132 -15
- nautobot/project-static/docs/development/core/testing.html +131 -14
- nautobot/project-static/docs/development/core/ui-component-framework.html +454 -283
- nautobot/project-static/docs/development/core/user-preferences.html +131 -14
- nautobot/project-static/docs/development/index.html +131 -14
- nautobot/project-static/docs/development/jobs/index.html +301 -132
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +131 -14
- nautobot/project-static/docs/index.html +139 -33
- 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 +132 -17
- nautobot/project-static/docs/overview/design_philosophy.html +131 -14
- nautobot/project-static/docs/release-notes/index.html +137 -22
- nautobot/project-static/docs/release-notes/version-1.0.html +319 -203
- nautobot/project-static/docs/release-notes/version-1.1.html +316 -200
- nautobot/project-static/docs/release-notes/version-1.2.html +391 -275
- nautobot/project-static/docs/release-notes/version-1.3.html +417 -301
- nautobot/project-static/docs/release-notes/version-1.4.html +502 -387
- nautobot/project-static/docs/release-notes/version-1.5.html +690 -576
- nautobot/project-static/docs/release-notes/version-1.6.html +989 -457
- nautobot/project-static/docs/release-notes/version-2.0.html +613 -499
- nautobot/project-static/docs/release-notes/version-2.1.html +448 -334
- nautobot/project-static/docs/release-notes/version-2.2.html +441 -327
- nautobot/project-static/docs/release-notes/version-2.3.html +1171 -451
- nautobot/project-static/docs/release-notes/version-2.4.html +800 -111
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +303 -287
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +131 -14
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +131 -14
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +133 -16
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +131 -14
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +131 -14
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +195 -18
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +134 -17
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +131 -14
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +133 -16
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +131 -14
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +131 -14
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +131 -14
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +151 -18
- nautobot/project-static/docs/user-guide/administration/installation/index.html +131 -14
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +132 -15
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +131 -14
- nautobot/project-static/docs/user-guide/administration/installation/services.html +131 -14
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +131 -14
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +131 -14
- nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
- nautobot/project-static/docs/user-guide/administration/security/notices.html +9843 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +131 -14
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +134 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +131 -14
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +135 -22
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +134 -17
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +131 -14
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +134 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +236 -34
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +131 -14
- 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 +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +131 -14
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +134 -17
- nautobot/project-static/docs/{development/core/local-k8s.html → user-guide/feature-guides/wireless-networks-and-controllers.html} +632 -566
- nautobot/project-static/docs/user-guide/index.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +135 -18
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +451 -16
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +135 -17
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +134 -17
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9797 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +132 -15
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +179 -35
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +159 -15
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +131 -14
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +131 -14
- nautobot/project-static/js/forms.js +1 -1
- nautobot/tenancy/api/views.py +9 -13
- nautobot/tenancy/templates/tenancy/tenant.html +1 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- 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.html +1 -1
- nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
- nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
- nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -10
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
- nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
- 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.2.dist-info}/METADATA +9 -9
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/RECORD +667 -610
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/WHEEL +1 -1
- nautobot/core/fixtures/user-data.json +0 -59
- /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import Any, Optional
|
|
2
3
|
|
|
3
4
|
from django.conf import settings
|
|
4
5
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
|
6
|
+
from django.db.models import Model
|
|
5
7
|
from django.test import override_settings, tag
|
|
6
8
|
from django.urls import reverse
|
|
7
9
|
from django.utils.functional import classproperty
|
|
8
10
|
from selenium.webdriver.common.keys import Keys
|
|
11
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
|
9
12
|
from splinter.browser import Browser
|
|
13
|
+
from splinter.exceptions import ElementDoesNotExist
|
|
10
14
|
|
|
11
15
|
from nautobot.core import testing
|
|
12
16
|
|
|
@@ -14,12 +18,171 @@ from nautobot.core import testing
|
|
|
14
18
|
SELENIUM_URL = os.getenv("NAUTOBOT_SELENIUM_URL", "http://localhost:4444/wd/hub")
|
|
15
19
|
|
|
16
20
|
# Hostname used by Selenium client to talk to Nautobot
|
|
17
|
-
SELENIUM_HOST = os.getenv("NAUTOBOT_SELENIUM_HOST", "
|
|
21
|
+
SELENIUM_HOST = os.getenv("NAUTOBOT_SELENIUM_HOST", "nautobot")
|
|
18
22
|
|
|
19
23
|
# Default login URL
|
|
20
24
|
LOGIN_URL = reverse(settings.LOGIN_URL)
|
|
21
25
|
|
|
22
26
|
|
|
27
|
+
class ObjectsListMixin:
|
|
28
|
+
"""
|
|
29
|
+
Helper class for easier testing and navigating on standard Nautobot objects list page.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def select_all_items(self):
|
|
33
|
+
"""
|
|
34
|
+
Click "toggle all" on top of the items table list to select all rows.
|
|
35
|
+
"""
|
|
36
|
+
self.browser.find_by_css("#object_list_form input.toggle").click()
|
|
37
|
+
|
|
38
|
+
def select_one_item(self):
|
|
39
|
+
"""
|
|
40
|
+
Click first row checkbox on items table list to select one row.
|
|
41
|
+
"""
|
|
42
|
+
self.browser.find_by_css('#object_list_form input[name="pk"]').click()
|
|
43
|
+
|
|
44
|
+
def set_per_page(self, per_page=1):
|
|
45
|
+
"""
|
|
46
|
+
Explicitly set the `per_page` parameter by navigating to the "current" page but with query param.
|
|
47
|
+
TODO: check if there are other query params and merge them
|
|
48
|
+
"""
|
|
49
|
+
self.browser.visit(f"{self.browser.url}?per_page={per_page}")
|
|
50
|
+
|
|
51
|
+
def select_all_items_from_all_pages(self):
|
|
52
|
+
"""
|
|
53
|
+
Selecting all the items from all pages by clicking "select all" on top of the items table list and then
|
|
54
|
+
select all on prompt that will show up.
|
|
55
|
+
"""
|
|
56
|
+
self.select_all_items()
|
|
57
|
+
self.browser.find_by_css("#select_all").click()
|
|
58
|
+
|
|
59
|
+
def click_bulk_delete(self):
|
|
60
|
+
"""
|
|
61
|
+
Click bulk delete from dropdown menu on bottom of the items table list.
|
|
62
|
+
"""
|
|
63
|
+
self.browser.execute_script(
|
|
64
|
+
"document.querySelector('#object_list_form button[type=\"submit\"]').scrollIntoView()"
|
|
65
|
+
)
|
|
66
|
+
self.browser.find_by_xpath(
|
|
67
|
+
'//*[@id="object_list_form"]//button[@type="submit"]/following-sibling::button[1]'
|
|
68
|
+
).click()
|
|
69
|
+
self.browser.find_by_css('#object_list_form button[name="_delete"]').click()
|
|
70
|
+
|
|
71
|
+
def click_bulk_delete_all(self):
|
|
72
|
+
"""
|
|
73
|
+
Click bulk delete all on prompt when selecting all items from all pages.
|
|
74
|
+
"""
|
|
75
|
+
self.click_button('#select_all_box button[name="_delete"]')
|
|
76
|
+
|
|
77
|
+
def click_bulk_edit(self):
|
|
78
|
+
"""
|
|
79
|
+
Click bulk edit button on bottom of the items table list.
|
|
80
|
+
"""
|
|
81
|
+
self.click_button('#object_list_form button[type="submit"]')
|
|
82
|
+
|
|
83
|
+
def click_bulk_edit_all(self):
|
|
84
|
+
"""
|
|
85
|
+
Click bulk edit all on prompt when selecting all items from all pages.
|
|
86
|
+
"""
|
|
87
|
+
self.click_button('#select_all_box button[name="_edit"]')
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def objects_list_visible_items(self):
|
|
91
|
+
"""
|
|
92
|
+
Calculating the visible items. Return 0 if there is no visible items.
|
|
93
|
+
"""
|
|
94
|
+
objects_table_container = self.browser.find_by_xpath('//*[@id="object_list_form"]')
|
|
95
|
+
try:
|
|
96
|
+
objects_table = objects_table_container.find_by_tag("tbody")
|
|
97
|
+
return len(objects_table.find_by_tag("tr"))
|
|
98
|
+
except ElementDoesNotExist:
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
def apply_filter(self, field, value):
|
|
102
|
+
"""
|
|
103
|
+
Open filter dialog and apply select2 filters.
|
|
104
|
+
You can apply more values to the same filter, by calling this function with same name but different value.
|
|
105
|
+
"""
|
|
106
|
+
self.browser.find_by_xpath('//*[@id="id__filterbtn"]').click()
|
|
107
|
+
self.fill_filters_select2_field(field, value)
|
|
108
|
+
self.click_button('#default-filter button[type="submit"]')
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class BulkOperationsMixin:
|
|
112
|
+
def confirm_bulk_delete_operation(self):
|
|
113
|
+
"""
|
|
114
|
+
Confirms bulk delete operation on the "warning" page after clicking bulk delete buttons.
|
|
115
|
+
"""
|
|
116
|
+
self.click_button('button[name="_confirm"][type="submit"]')
|
|
117
|
+
|
|
118
|
+
def submit_bulk_edit_operation(self):
|
|
119
|
+
"""
|
|
120
|
+
Submits the bulk edit form.
|
|
121
|
+
"""
|
|
122
|
+
self.click_button('button[name="_apply"]')
|
|
123
|
+
|
|
124
|
+
def wait_for_job_result(self):
|
|
125
|
+
"""
|
|
126
|
+
Waits 30s for job to be finished.
|
|
127
|
+
"""
|
|
128
|
+
end_statuses = ["Completed", "Failed"]
|
|
129
|
+
WebDriverWait(self.browser, 30).until(
|
|
130
|
+
lambda driver: driver.find_by_id("pending-result-label").text in end_statuses
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return self.browser.find_by_id("pending-result-label").text
|
|
134
|
+
|
|
135
|
+
def verify_job_description(self, expected_job_description):
|
|
136
|
+
"""
|
|
137
|
+
Verifies if the job description is correct.
|
|
138
|
+
Waits 30s on page load in case of large payload being sent from bulk edit form.
|
|
139
|
+
"""
|
|
140
|
+
WebDriverWait(self.browser, 30).until(lambda driver: driver.is_text_present("Job Description"))
|
|
141
|
+
|
|
142
|
+
job_description = self.browser.find_by_xpath('//td[text()="Job Description"]/following-sibling::td[1]').text
|
|
143
|
+
self.assertEqual(job_description, expected_job_description)
|
|
144
|
+
|
|
145
|
+
def update_edit_form_value(self, field_name, value, is_select=False):
|
|
146
|
+
"""
|
|
147
|
+
Updates bulk edit form value.
|
|
148
|
+
"""
|
|
149
|
+
if is_select:
|
|
150
|
+
self.fill_select2_field(field_name, value)
|
|
151
|
+
else:
|
|
152
|
+
self.browser.fill(field_name, value)
|
|
153
|
+
|
|
154
|
+
def assertBulkDeleteConfirmMessageIsValid(self, expected_count):
|
|
155
|
+
"""
|
|
156
|
+
Asserts that bulk delete confirmation message is valid and if we're deleting proper number of items.
|
|
157
|
+
"""
|
|
158
|
+
self.browser.is_element_present_by_tag("body", wait_time=30)
|
|
159
|
+
|
|
160
|
+
button_text = self.browser.find_by_xpath('//button[@name="_confirm" and @type="submit"]').text
|
|
161
|
+
self.assertIn(f"Delete these {expected_count}", button_text)
|
|
162
|
+
|
|
163
|
+
message_text = self.browser.find_by_id("confirm-bulk-deletion").find_by_xpath('//div[@class="panel-body"]').text
|
|
164
|
+
self.assertIn(f"The following operation will delete {expected_count}", message_text)
|
|
165
|
+
|
|
166
|
+
def assertIsBulkDeleteJob(self):
|
|
167
|
+
"""
|
|
168
|
+
Asserts if currently visible job is bulk delete job.
|
|
169
|
+
"""
|
|
170
|
+
self.verify_job_description("Bulk delete objects.")
|
|
171
|
+
|
|
172
|
+
def assertIsBulkEditJob(self):
|
|
173
|
+
"""
|
|
174
|
+
Asserts if currently visible job is bulk edit job.
|
|
175
|
+
"""
|
|
176
|
+
self.verify_job_description("Bulk edit objects.")
|
|
177
|
+
|
|
178
|
+
def assertJobStatusIsCompleted(self):
|
|
179
|
+
"""
|
|
180
|
+
Asserts that job was successfully completed.
|
|
181
|
+
"""
|
|
182
|
+
job_status = self.wait_for_job_result()
|
|
183
|
+
self.assertEqual(job_status, "Completed")
|
|
184
|
+
|
|
185
|
+
|
|
23
186
|
# In CI, sometimes the FQDN of SELENIUM_HOST gets used, other times it seems to be just the hostname?
|
|
24
187
|
@override_settings(ALLOWED_HOSTS=["nautobot.example.com", SELENIUM_HOST, SELENIUM_HOST.split(".")[0]])
|
|
25
188
|
@tag("integration")
|
|
@@ -112,21 +275,36 @@ class SeleniumTestCase(StaticLiveServerTestCase, testing.NautobotTestCaseMixin):
|
|
|
112
275
|
# Wait for body element to appear
|
|
113
276
|
self.assertTrue(self.browser.is_element_present_by_tag("body", wait_time=5), "Page failed to load")
|
|
114
277
|
|
|
115
|
-
def
|
|
278
|
+
def _fill_select2_field(self, field_name, value, search_box_class=None):
|
|
116
279
|
"""
|
|
117
280
|
Helper function to fill a Select2 single selection field.
|
|
118
281
|
"""
|
|
282
|
+
if search_box_class is None:
|
|
283
|
+
search_box_class = "select2-search select2-search--dropdown"
|
|
284
|
+
|
|
119
285
|
self.browser.find_by_xpath(f"//select[@id='id_{field_name}']//following-sibling::span").click()
|
|
120
|
-
search_box = self.browser.find_by_xpath(
|
|
121
|
-
"//*[@class='select2-search select2-search--dropdown']//input", wait_time=5
|
|
122
|
-
)
|
|
286
|
+
search_box = self.browser.find_by_xpath(f"//*[@class='{search_box_class}']//input", wait_time=5)
|
|
123
287
|
for _ in search_box.first.type(value, slowly=True):
|
|
124
288
|
pass
|
|
125
289
|
|
|
126
290
|
# wait for "searching" to disappear
|
|
127
291
|
self.browser.is_element_not_present_by_css(".loading-results", wait_time=5)
|
|
292
|
+
return search_box
|
|
293
|
+
|
|
294
|
+
def fill_select2_field(self, field_name, value):
|
|
295
|
+
"""
|
|
296
|
+
Helper function to fill a Select2 single selection field on add/edit forms.
|
|
297
|
+
"""
|
|
298
|
+
search_box = self._fill_select2_field(field_name, value)
|
|
128
299
|
search_box.first.type(Keys.ENTER)
|
|
129
300
|
|
|
301
|
+
def fill_filters_select2_field(self, field_name, value):
|
|
302
|
+
"""
|
|
303
|
+
Helper function to fill a Select2 single selection field on filters modals.
|
|
304
|
+
"""
|
|
305
|
+
self._fill_select2_field(field_name, value, search_box_class="select2-search select2-search--inline")
|
|
306
|
+
self.browser.find_by_xpath(f"//li[contains(@class, 'select2-results__option') and text()='{value}']").click()
|
|
307
|
+
|
|
130
308
|
def fill_select2_multiselect_field(self, field_name, value):
|
|
131
309
|
"""
|
|
132
310
|
Helper function to fill a Select2 multi-selection field.
|
|
@@ -138,3 +316,334 @@ class SeleniumTestCase(StaticLiveServerTestCase, testing.NautobotTestCaseMixin):
|
|
|
138
316
|
# wait for "searching" to disappear
|
|
139
317
|
self.browser.is_element_not_present_by_css(".loading-results", wait_time=5)
|
|
140
318
|
search_box.first.type(Keys.ENTER)
|
|
319
|
+
|
|
320
|
+
def click_button(self, query_selector):
|
|
321
|
+
btn = self.browser.find_by_css(query_selector, wait_time=5)
|
|
322
|
+
# Button might be visible but on the edge and then impossible to click due to vertical/horizontal scrolls
|
|
323
|
+
self.browser.execute_script(f"document.querySelector('{query_selector}').scrollIntoView()")
|
|
324
|
+
btn.click()
|
|
325
|
+
|
|
326
|
+
def login_as_superuser(self):
|
|
327
|
+
self.user.is_superuser = True
|
|
328
|
+
self.user.save()
|
|
329
|
+
self.login(self.user.username, self.password)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class BulkOperationsTestCases:
|
|
333
|
+
"""
|
|
334
|
+
Helper classes that runs all the basic bulk-operations test cases like edit/delete with
|
|
335
|
+
filtered / not filtered items along with select all option.
|
|
336
|
+
|
|
337
|
+
To use this class create required items in setUp method:
|
|
338
|
+
- at least four entities in two different groups,
|
|
339
|
+
- provide field for filtering to distinguish between above two groups
|
|
340
|
+
- provide expected counts (if different from default)
|
|
341
|
+
- set edit field and value
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
class BaseTestCase(SeleniumTestCase):
|
|
345
|
+
model_menu_path: tuple[str, str]
|
|
346
|
+
model_base_viewname: str
|
|
347
|
+
model_edit_data: dict[str, Any]
|
|
348
|
+
model_filter_by: dict[str, Any]
|
|
349
|
+
model_class: type[Model]
|
|
350
|
+
override_model_plural: Optional[str] = None
|
|
351
|
+
model_expected_counts: dict[str, int] = {
|
|
352
|
+
"all": 5,
|
|
353
|
+
"filtered": 2,
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def model_plural(self) -> str:
|
|
358
|
+
if self.override_model_plural is None:
|
|
359
|
+
return self.model_class._meta.verbose_name_plural
|
|
360
|
+
|
|
361
|
+
return self.override_model_plural
|
|
362
|
+
|
|
363
|
+
def setUp(self):
|
|
364
|
+
super().setUp()
|
|
365
|
+
|
|
366
|
+
self.setup_items()
|
|
367
|
+
self.login_as_superuser()
|
|
368
|
+
self.go_to_model_list_page()
|
|
369
|
+
|
|
370
|
+
def tearDown(self):
|
|
371
|
+
self.logout()
|
|
372
|
+
super().tearDown()
|
|
373
|
+
|
|
374
|
+
def go_to_model_list_page(self):
|
|
375
|
+
self.click_navbar_entry(*self.model_menu_path)
|
|
376
|
+
self.assertEqual(self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_list"))
|
|
377
|
+
|
|
378
|
+
def setup_items(self):
|
|
379
|
+
raise NotImplementedError
|
|
380
|
+
|
|
381
|
+
class BulkEditTestCase(BaseTestCase, ObjectsListMixin, BulkOperationsMixin):
|
|
382
|
+
def test_bulk_edit_require_selection(self):
|
|
383
|
+
# Click "edit selected" without selecting anything
|
|
384
|
+
self.click_bulk_edit()
|
|
385
|
+
|
|
386
|
+
self.assertEqual(self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_list"))
|
|
387
|
+
self.assertTrue(self.browser.is_text_present(f"No {self.model_plural} were selected", wait_time=5))
|
|
388
|
+
|
|
389
|
+
def test_bulk_edit_all_items(self):
|
|
390
|
+
# Select all items and edit them
|
|
391
|
+
self.select_all_items()
|
|
392
|
+
self.click_bulk_edit()
|
|
393
|
+
self.assertEqual(self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_edit"))
|
|
394
|
+
|
|
395
|
+
# Edit some data and submit the form
|
|
396
|
+
for field_name, field_value in self.model_edit_data.items():
|
|
397
|
+
self.update_edit_form_value(field_name, field_value)
|
|
398
|
+
self.submit_bulk_edit_operation()
|
|
399
|
+
|
|
400
|
+
# Verify job output
|
|
401
|
+
self.assertIsBulkEditJob()
|
|
402
|
+
self.assertJobStatusIsCompleted()
|
|
403
|
+
|
|
404
|
+
# Assert that data was changed
|
|
405
|
+
found_items = self.model_class.objects.filter(**self.model_edit_data).count()
|
|
406
|
+
self.assertEqual(found_items, self.model_expected_counts["all"])
|
|
407
|
+
|
|
408
|
+
def test_bulk_edit_one_item(self):
|
|
409
|
+
# Select one filtered item
|
|
410
|
+
self.select_one_item()
|
|
411
|
+
self.click_bulk_edit()
|
|
412
|
+
self.assertEqual(self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_edit"))
|
|
413
|
+
|
|
414
|
+
# Edit some data and submit the form
|
|
415
|
+
for field_name, field_value in self.model_edit_data.items():
|
|
416
|
+
self.update_edit_form_value(field_name, field_value)
|
|
417
|
+
self.submit_bulk_edit_operation()
|
|
418
|
+
|
|
419
|
+
# Verify job output
|
|
420
|
+
self.assertIsBulkEditJob()
|
|
421
|
+
self.assertJobStatusIsCompleted()
|
|
422
|
+
|
|
423
|
+
# Assert that data was changed
|
|
424
|
+
found_items = self.model_class.objects.filter(**self.model_edit_data).count()
|
|
425
|
+
self.assertEqual(found_items, 1)
|
|
426
|
+
|
|
427
|
+
def test_bulk_edit_all_items_from_all_pages(self):
|
|
428
|
+
# Select all from all pages
|
|
429
|
+
self.set_per_page()
|
|
430
|
+
self.select_all_items_from_all_pages()
|
|
431
|
+
self.click_bulk_edit_all()
|
|
432
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_edit"), self.browser.url)
|
|
433
|
+
|
|
434
|
+
# Edit some data and submit the form
|
|
435
|
+
for field_name, field_value in self.model_edit_data.items():
|
|
436
|
+
self.update_edit_form_value(field_name, field_value)
|
|
437
|
+
self.submit_bulk_edit_operation()
|
|
438
|
+
|
|
439
|
+
# Verify job output
|
|
440
|
+
self.assertIsBulkEditJob()
|
|
441
|
+
self.assertJobStatusIsCompleted()
|
|
442
|
+
|
|
443
|
+
# Assert that data was changed
|
|
444
|
+
found_items = self.model_class.objects.filter(**self.model_edit_data).count()
|
|
445
|
+
self.assertEqual(found_items, self.model_expected_counts["all"])
|
|
446
|
+
|
|
447
|
+
def test_bulk_edit_all_filtered_items(self):
|
|
448
|
+
# Filter items
|
|
449
|
+
for field, value in self.model_filter_by.items():
|
|
450
|
+
self.apply_filter(field, value)
|
|
451
|
+
|
|
452
|
+
# Select all filtered items
|
|
453
|
+
self.select_all_items()
|
|
454
|
+
self.click_bulk_edit()
|
|
455
|
+
self.assertIn(
|
|
456
|
+
self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_edit"),
|
|
457
|
+
self.browser.url,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
# Edit some data and submit the form
|
|
461
|
+
for field_name, field_value in self.model_edit_data.items():
|
|
462
|
+
self.update_edit_form_value(field_name, field_value)
|
|
463
|
+
self.submit_bulk_edit_operation()
|
|
464
|
+
|
|
465
|
+
# Verify job output
|
|
466
|
+
self.assertIsBulkEditJob()
|
|
467
|
+
self.assertJobStatusIsCompleted()
|
|
468
|
+
|
|
469
|
+
# Assert that data was changed
|
|
470
|
+
found_items = self.model_class.objects.filter(**self.model_edit_data).count()
|
|
471
|
+
self.assertEqual(found_items, self.model_expected_counts["filtered"])
|
|
472
|
+
|
|
473
|
+
def test_bulk_edit_one_filtered_item(self):
|
|
474
|
+
# Filter items
|
|
475
|
+
for field, value in self.model_filter_by.items():
|
|
476
|
+
self.apply_filter(field, value)
|
|
477
|
+
|
|
478
|
+
# Select one item and edit it
|
|
479
|
+
self.select_one_item()
|
|
480
|
+
self.click_bulk_edit()
|
|
481
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_edit"), self.browser.url)
|
|
482
|
+
|
|
483
|
+
# Edit some data and submit the form
|
|
484
|
+
for field_name, field_value in self.model_edit_data.items():
|
|
485
|
+
self.update_edit_form_value(field_name, field_value)
|
|
486
|
+
self.submit_bulk_edit_operation()
|
|
487
|
+
|
|
488
|
+
# Verify job output
|
|
489
|
+
self.assertIsBulkEditJob()
|
|
490
|
+
self.assertJobStatusIsCompleted()
|
|
491
|
+
|
|
492
|
+
# Assert that data was changed
|
|
493
|
+
found_items = self.model_class.objects.filter(**self.model_edit_data).count()
|
|
494
|
+
self.assertEqual(found_items, 1)
|
|
495
|
+
|
|
496
|
+
def test_bulk_edit_all_filtered_items_from_all_pages(self):
|
|
497
|
+
# Filter items
|
|
498
|
+
self.set_per_page()
|
|
499
|
+
for field, value in self.model_filter_by.items():
|
|
500
|
+
self.apply_filter(field, value)
|
|
501
|
+
|
|
502
|
+
# Select all items and delete them
|
|
503
|
+
self.select_all_items_from_all_pages()
|
|
504
|
+
self.click_bulk_edit_all()
|
|
505
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_edit"), self.browser.url)
|
|
506
|
+
|
|
507
|
+
# Edit some data and submit the form
|
|
508
|
+
for field_name, field_value in self.model_edit_data.items():
|
|
509
|
+
self.update_edit_form_value(field_name, field_value)
|
|
510
|
+
self.submit_bulk_edit_operation()
|
|
511
|
+
|
|
512
|
+
# Verify job output
|
|
513
|
+
self.assertIsBulkEditJob()
|
|
514
|
+
self.assertJobStatusIsCompleted()
|
|
515
|
+
|
|
516
|
+
# Assert that data was changed
|
|
517
|
+
found_items = self.model_class.objects.filter(**self.model_edit_data).count()
|
|
518
|
+
self.assertEqual(found_items, self.model_expected_counts["filtered"])
|
|
519
|
+
|
|
520
|
+
class BulkDeleteTestCase(BaseTestCase, ObjectsListMixin, BulkOperationsMixin):
|
|
521
|
+
def test_bulk_delete_require_selection(self):
|
|
522
|
+
# Click "delete selected" without selecting anything
|
|
523
|
+
self.click_bulk_delete()
|
|
524
|
+
|
|
525
|
+
self.assertEqual(self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_list"))
|
|
526
|
+
self.assertTrue(
|
|
527
|
+
self.browser.is_text_present(f"No {self.model_plural} were selected for deletion.", wait_time=5)
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
def test_bulk_delete_all_items(self):
|
|
531
|
+
# Select all items and delete them
|
|
532
|
+
self.select_all_items()
|
|
533
|
+
self.click_bulk_delete()
|
|
534
|
+
|
|
535
|
+
self.assertEqual(
|
|
536
|
+
self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_delete")
|
|
537
|
+
)
|
|
538
|
+
self.assertBulkDeleteConfirmMessageIsValid(self.model_expected_counts["all"])
|
|
539
|
+
self.confirm_bulk_delete_operation()
|
|
540
|
+
|
|
541
|
+
# Verify job output
|
|
542
|
+
self.assertIsBulkDeleteJob()
|
|
543
|
+
self.assertJobStatusIsCompleted()
|
|
544
|
+
|
|
545
|
+
self.go_to_model_list_page()
|
|
546
|
+
self.assertEqual(self.objects_list_visible_items, 0)
|
|
547
|
+
|
|
548
|
+
def test_bulk_delete_one_item(self):
|
|
549
|
+
# Select one item and delete it
|
|
550
|
+
self.select_one_item()
|
|
551
|
+
self.click_bulk_delete()
|
|
552
|
+
|
|
553
|
+
self.assertEqual(
|
|
554
|
+
self.browser.url, self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_delete")
|
|
555
|
+
)
|
|
556
|
+
self.assertBulkDeleteConfirmMessageIsValid(1)
|
|
557
|
+
self.confirm_bulk_delete_operation()
|
|
558
|
+
|
|
559
|
+
# Verify job output
|
|
560
|
+
self.assertIsBulkDeleteJob()
|
|
561
|
+
self.assertJobStatusIsCompleted()
|
|
562
|
+
|
|
563
|
+
self.go_to_model_list_page()
|
|
564
|
+
self.assertEqual(self.objects_list_visible_items, self.model_expected_counts["all"] - 1)
|
|
565
|
+
|
|
566
|
+
def test_bulk_delete_all_items_from_all_pages(self):
|
|
567
|
+
# Select all from all pages
|
|
568
|
+
self.set_per_page()
|
|
569
|
+
self.select_all_items_from_all_pages()
|
|
570
|
+
self.click_bulk_delete_all()
|
|
571
|
+
|
|
572
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_delete"), self.browser.url)
|
|
573
|
+
self.assertBulkDeleteConfirmMessageIsValid(self.model_expected_counts["all"])
|
|
574
|
+
self.confirm_bulk_delete_operation()
|
|
575
|
+
|
|
576
|
+
# Verify job output
|
|
577
|
+
self.assertIsBulkDeleteJob()
|
|
578
|
+
self.assertJobStatusIsCompleted()
|
|
579
|
+
|
|
580
|
+
self.go_to_model_list_page()
|
|
581
|
+
self.assertEqual(self.objects_list_visible_items, 0)
|
|
582
|
+
|
|
583
|
+
def test_bulk_delete_all_filtered_items(self):
|
|
584
|
+
# Filter items
|
|
585
|
+
for field, value in self.model_filter_by.items():
|
|
586
|
+
self.apply_filter(field, value)
|
|
587
|
+
|
|
588
|
+
# Select all items and delete them
|
|
589
|
+
self.select_all_items()
|
|
590
|
+
self.click_bulk_delete()
|
|
591
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_delete"), self.browser.url)
|
|
592
|
+
self.confirm_bulk_delete_operation()
|
|
593
|
+
|
|
594
|
+
# Verify job output
|
|
595
|
+
self.assertIsBulkDeleteJob()
|
|
596
|
+
self.assertJobStatusIsCompleted()
|
|
597
|
+
|
|
598
|
+
self.go_to_model_list_page()
|
|
599
|
+
rest_items_count = self.model_expected_counts["all"] - self.model_expected_counts["filtered"]
|
|
600
|
+
self.assertEqual(self.objects_list_visible_items, rest_items_count)
|
|
601
|
+
|
|
602
|
+
def test_bulk_delete_one_filtered_items(self):
|
|
603
|
+
# Filter items
|
|
604
|
+
for field, value in self.model_filter_by.items():
|
|
605
|
+
self.apply_filter(field, value)
|
|
606
|
+
|
|
607
|
+
# Select one item and delete it
|
|
608
|
+
self.select_one_item()
|
|
609
|
+
self.click_bulk_delete()
|
|
610
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_delete"), self.browser.url)
|
|
611
|
+
self.confirm_bulk_delete_operation()
|
|
612
|
+
|
|
613
|
+
# Verify job output
|
|
614
|
+
self.assertIsBulkDeleteJob()
|
|
615
|
+
self.assertJobStatusIsCompleted()
|
|
616
|
+
|
|
617
|
+
self.go_to_model_list_page()
|
|
618
|
+
self.assertEqual(self.objects_list_visible_items, self.model_expected_counts["all"] - 1)
|
|
619
|
+
|
|
620
|
+
def test_bulk_delete_all_filtered_items_from_all_pages(self):
|
|
621
|
+
# Filter items
|
|
622
|
+
self.set_per_page()
|
|
623
|
+
for field, value in self.model_filter_by.items():
|
|
624
|
+
self.apply_filter(field, value)
|
|
625
|
+
|
|
626
|
+
# Select all items and delete them
|
|
627
|
+
self.select_all_items_from_all_pages()
|
|
628
|
+
self.click_bulk_delete_all()
|
|
629
|
+
|
|
630
|
+
self.assertIn(self.live_server_url + reverse(f"{self.model_base_viewname}_bulk_delete"), self.browser.url)
|
|
631
|
+
self.assertBulkDeleteConfirmMessageIsValid(self.model_expected_counts["filtered"])
|
|
632
|
+
self.confirm_bulk_delete_operation()
|
|
633
|
+
|
|
634
|
+
# Verify job output
|
|
635
|
+
self.assertIsBulkDeleteJob()
|
|
636
|
+
self.assertJobStatusIsCompleted()
|
|
637
|
+
|
|
638
|
+
self.go_to_model_list_page()
|
|
639
|
+
self.set_per_page(50) # Set page size back to default
|
|
640
|
+
rest_items_count = self.model_expected_counts["all"] - self.model_expected_counts["filtered"]
|
|
641
|
+
self.assertEqual(self.objects_list_visible_items, rest_items_count)
|
|
642
|
+
|
|
643
|
+
# Filter again and assert that all items were deleted
|
|
644
|
+
for field, value in self.model_filter_by.items():
|
|
645
|
+
self.apply_filter(field, value)
|
|
646
|
+
self.assertEqual(self.objects_list_visible_items, 0)
|
|
647
|
+
|
|
648
|
+
class BulkOperationsTestCase(BulkEditTestCase, BulkDeleteTestCase):
|
|
649
|
+
pass
|
nautobot/core/testing/mixins.py
CHANGED
|
@@ -144,13 +144,18 @@ class NautobotTestCaseMixin:
|
|
|
144
144
|
# Permissions management
|
|
145
145
|
#
|
|
146
146
|
|
|
147
|
-
def add_permissions(self, *names):
|
|
147
|
+
def add_permissions(self, *names, **kwargs):
|
|
148
148
|
"""
|
|
149
149
|
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
|
|
150
|
+
Additional keyword arguments will be passed to the ObjectPermission constructor to allow creating more detailed permissions.
|
|
151
|
+
|
|
152
|
+
Examples:
|
|
153
|
+
>>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup")
|
|
154
|
+
>>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup", constraints={"pk": "uuid-1234"})
|
|
150
155
|
"""
|
|
151
156
|
for name in names:
|
|
152
157
|
ct, action = permissions.resolve_permission_ct(name)
|
|
153
|
-
obj_perm = users_models.ObjectPermission(name=name, actions=[action])
|
|
158
|
+
obj_perm = users_models.ObjectPermission(name=name, actions=[action], **kwargs)
|
|
154
159
|
obj_perm.save()
|
|
155
160
|
obj_perm.users.add(self.user)
|
|
156
161
|
obj_perm.object_types.add(ct)
|