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
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
SOURCE_DIR = os.path.join(os.path.dirname(__file__), "git_data")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def create_and_populate_git_repository(target_path):
|
|
17
|
+
def create_and_populate_git_repository(target_path, divergent_branch=None):
|
|
18
18
|
"""
|
|
19
19
|
Create a Git repository in `target_path` and populate it with commits and tags based on contents of `SOURCE_DIR`.
|
|
20
20
|
|
|
@@ -40,6 +40,9 @@ def create_and_populate_git_repository(target_path):
|
|
|
40
40
|
Note that each commit is fully defined by the files in the appropriate subdirectory; if you want a file to exist
|
|
41
41
|
across multiple separate commits, it must exist in multiple subdirectories. Use of symlinks is encouraged in such
|
|
42
42
|
a scenario.
|
|
43
|
+
|
|
44
|
+
You can optionally create and check out a divergent branch from the main branch by passing a branch name as the `divergent_branch`.
|
|
45
|
+
This will write a commit to the divergent branch and tag it with the branch name with the `-tag` suffix.
|
|
43
46
|
"""
|
|
44
47
|
os.makedirs(target_path, exist_ok=True)
|
|
45
48
|
repo = Repo.init(target_path, initial_branch="main")
|
|
@@ -69,6 +72,11 @@ def create_and_populate_git_repository(target_path):
|
|
|
69
72
|
# Directory "01-valid-files" --> tag "valid-files" so that we won't break tests if we renumber the directories
|
|
70
73
|
repo.create_tag(dirname[3:], message=f"Tag based on {dirname} files")
|
|
71
74
|
|
|
75
|
+
if divergent_branch:
|
|
76
|
+
repo.create_head(divergent_branch)
|
|
77
|
+
repo.index.commit("divergent-branch")
|
|
78
|
+
repo.create_tag(f"{divergent_branch}-tag", message=f"Tag for divergent branch {divergent_branch}")
|
|
79
|
+
|
|
72
80
|
|
|
73
81
|
if __name__ == "__main__":
|
|
74
82
|
directory_path = tempfile.TemporaryDirectory().name # pylint: disable=consider-using-with
|
|
@@ -6,28 +6,41 @@ from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Man
|
|
|
6
6
|
from nautobot.extras.models import Role, Status
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def create_test_device():
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
def create_test_device(name=None, location_name=None, test_uuid=None):
|
|
10
|
+
if not test_uuid:
|
|
11
|
+
test_uuid = str(uuid.uuid4())
|
|
12
|
+
if not name:
|
|
13
|
+
name = f"Test Device {test_uuid}"
|
|
14
|
+
if not location_name:
|
|
15
|
+
location_name = f"Test Location {test_uuid}"
|
|
16
|
+
|
|
17
|
+
location_type, location_type_created = LocationType.objects.get_or_create(name=f"Test Location Type {test_uuid}")
|
|
18
|
+
if location_type_created:
|
|
19
|
+
location_type.content_types.add(ContentType.objects.get_for_model(Device))
|
|
20
|
+
location_type.save()
|
|
21
|
+
|
|
20
22
|
location_status = Status.objects.get_for_model(Location).first()
|
|
21
|
-
location = Location.objects.
|
|
22
|
-
name=
|
|
23
|
+
location, _ = Location.objects.get_or_create(
|
|
24
|
+
name=location_name,
|
|
23
25
|
status=location_status,
|
|
24
26
|
location_type=location_type,
|
|
25
27
|
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
device_role, device_role_created = Role.objects.get_or_create(name="Device Role")
|
|
30
|
+
if device_role_created:
|
|
31
|
+
device_role.content_types.add(ContentType.objects.get_for_model(Device))
|
|
32
|
+
device_role.save()
|
|
33
|
+
|
|
34
|
+
manufacturer, _ = Manufacturer.objects.get_or_create(
|
|
35
|
+
name=f"Test Manufacturer {test_uuid}",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
device_type, _ = DeviceType.objects.get_or_create(manufacturer=manufacturer, model=f"Test Model {test_uuid}")
|
|
39
|
+
|
|
40
|
+
return Device.objects.create(
|
|
41
|
+
name=name,
|
|
28
42
|
role=device_role,
|
|
29
43
|
device_type=device_type,
|
|
30
44
|
location=location,
|
|
31
45
|
status=location_status,
|
|
32
46
|
)
|
|
33
|
-
return device
|
|
@@ -4,8 +4,6 @@ from nautobot.core.testing.integration import SeleniumTestCase
|
|
|
4
4
|
class PluginBannerTestCase(SeleniumTestCase):
|
|
5
5
|
"""Integration test for rendering of plugin-injected banner content."""
|
|
6
6
|
|
|
7
|
-
fixtures = ("user-data",)
|
|
8
|
-
|
|
9
7
|
def test_banner_not_rendered(self):
|
|
10
8
|
"""As implemented, plugin banner does not render if the user is not logged in.
|
|
11
9
|
|
|
@@ -1207,6 +1207,10 @@ class GitRepositoryTest(APIViewTestCases.APIViewTestCase):
|
|
|
1207
1207
|
url = reverse("extras-api:gitrepository-sync", kwargs={"pk": self.repos[0].id})
|
|
1208
1208
|
response = self.client.post(url, format="json", **self.header)
|
|
1209
1209
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1210
|
+
self.assertIn("message", response.data)
|
|
1211
|
+
self.assertIn("job_result", response.data)
|
|
1212
|
+
self.assertEqual(response.data["message"], f"Repository {self.repos[0].name} sync job added to queue.")
|
|
1213
|
+
self.assertIsInstance(response.data["job_result"], dict)
|
|
1210
1214
|
|
|
1211
1215
|
def test_create_with_app_provided_contents(self):
|
|
1212
1216
|
"""Test that `provided_contents` published by an App works."""
|
|
@@ -1241,6 +1245,8 @@ class GraphQLQueryTest(APIViewTestCases.APIViewTestCase):
|
|
|
1241
1245
|
},
|
|
1242
1246
|
]
|
|
1243
1247
|
|
|
1248
|
+
choices_fields = ["owner_content_type"]
|
|
1249
|
+
|
|
1244
1250
|
@classmethod
|
|
1245
1251
|
def setUpTestData(cls):
|
|
1246
1252
|
cls.graphqlqueries = (
|
|
@@ -1536,10 +1542,10 @@ class JobTest(
|
|
|
1536
1542
|
self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND)
|
|
1537
1543
|
|
|
1538
1544
|
# Try post to permitted job
|
|
1539
|
-
job_model = Job.objects.get_for_class_path("pass.
|
|
1545
|
+
job_model = Job.objects.get_for_class_path("pass.TestPassJob")
|
|
1540
1546
|
job_model.enabled = True
|
|
1541
1547
|
job_model.validated_save()
|
|
1542
|
-
url = self.get_run_url("pass.
|
|
1548
|
+
url = self.get_run_url("pass.TestPassJob")
|
|
1543
1549
|
response = self.client.post(url, **self.header)
|
|
1544
1550
|
self.assertHttpStatus(response, self.run_success_response_status)
|
|
1545
1551
|
|
|
@@ -2129,7 +2135,7 @@ class JobTest(
|
|
|
2129
2135
|
@mock.patch("nautobot.extras.api.views.get_worker_count", return_value=1)
|
|
2130
2136
|
def test_run_job_with_default_queue_with_empty_job_model_job_queues(self, _):
|
|
2131
2137
|
self.add_permissions("extras.run_job")
|
|
2132
|
-
job_model = Job.objects.get_for_class_path("pass.
|
|
2138
|
+
job_model = Job.objects.get_for_class_path("pass.TestPassJob")
|
|
2133
2139
|
data = {
|
|
2134
2140
|
"task_queue": job_model.default_job_queue.name,
|
|
2135
2141
|
}
|
|
@@ -2137,7 +2143,7 @@ class JobTest(
|
|
|
2137
2143
|
job_model.job_queues.set([])
|
|
2138
2144
|
job_model.enabled = True
|
|
2139
2145
|
job_model.validated_save()
|
|
2140
|
-
url = self.get_run_url("pass.
|
|
2146
|
+
url = self.get_run_url("pass.TestPassJob")
|
|
2141
2147
|
response = self.client.post(url, data, format="json", **self.header)
|
|
2142
2148
|
self.assertHttpStatus(response, self.run_success_response_status)
|
|
2143
2149
|
|
|
@@ -2564,10 +2570,10 @@ class ScheduledJobTest(
|
|
|
2564
2570
|
@classmethod
|
|
2565
2571
|
def setUpTestData(cls):
|
|
2566
2572
|
user = User.objects.create(username="user1", is_active=True)
|
|
2567
|
-
job_model = Job.objects.get_for_class_path("pass.
|
|
2573
|
+
job_model = Job.objects.get_for_class_path("pass.TestPassJob")
|
|
2568
2574
|
ScheduledJob.objects.create(
|
|
2569
2575
|
name="test1",
|
|
2570
|
-
task="pass.
|
|
2576
|
+
task="pass.TestPassJob",
|
|
2571
2577
|
job_model=job_model,
|
|
2572
2578
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
2573
2579
|
user=user,
|
|
@@ -2576,7 +2582,7 @@ class ScheduledJobTest(
|
|
|
2576
2582
|
)
|
|
2577
2583
|
ScheduledJob.objects.create(
|
|
2578
2584
|
name="test2",
|
|
2579
|
-
task="pass.
|
|
2585
|
+
task="pass.TestPassJob",
|
|
2580
2586
|
job_model=job_model,
|
|
2581
2587
|
interval=JobExecutionType.TYPE_DAILY,
|
|
2582
2588
|
user=user,
|
|
@@ -2586,7 +2592,7 @@ class ScheduledJobTest(
|
|
|
2586
2592
|
)
|
|
2587
2593
|
ScheduledJob.objects.create(
|
|
2588
2594
|
name="test3",
|
|
2589
|
-
task="pass.
|
|
2595
|
+
task="pass.TestPassJob",
|
|
2590
2596
|
job_model=job_model,
|
|
2591
2597
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
2592
2598
|
crontab="34 12 * * *",
|
|
@@ -2601,12 +2607,12 @@ class JobApprovalTest(APITestCase):
|
|
|
2601
2607
|
@classmethod
|
|
2602
2608
|
def setUpTestData(cls):
|
|
2603
2609
|
cls.additional_user = User.objects.create(username="user1", is_active=True)
|
|
2604
|
-
cls.job_model = Job.objects.get_for_class_path("pass.
|
|
2610
|
+
cls.job_model = Job.objects.get_for_class_path("pass.TestPassJob")
|
|
2605
2611
|
cls.job_model.enabled = True
|
|
2606
2612
|
cls.job_model.save()
|
|
2607
2613
|
cls.scheduled_job = ScheduledJob.objects.create(
|
|
2608
2614
|
name="test pass",
|
|
2609
|
-
task="pass.
|
|
2615
|
+
task="pass.TestPassJob",
|
|
2610
2616
|
job_model=cls.job_model,
|
|
2611
2617
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
2612
2618
|
user=cls.additional_user,
|
|
@@ -2659,7 +2665,7 @@ class JobApprovalTest(APITestCase):
|
|
|
2659
2665
|
self.add_permissions("extras.approve_job", "extras.view_scheduledjob", "extras.change_scheduledjob")
|
|
2660
2666
|
scheduled_job = ScheduledJob.objects.create(
|
|
2661
2667
|
name="test",
|
|
2662
|
-
task="pass.
|
|
2668
|
+
task="pass.TestPassJob",
|
|
2663
2669
|
job_model=self.job_model,
|
|
2664
2670
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
2665
2671
|
user=self.user,
|
|
@@ -2682,7 +2688,7 @@ class JobApprovalTest(APITestCase):
|
|
|
2682
2688
|
self.add_permissions("extras.approve_job", "extras.view_scheduledjob", "extras.change_scheduledjob")
|
|
2683
2689
|
scheduled_job = ScheduledJob.objects.create(
|
|
2684
2690
|
name="test",
|
|
2685
|
-
task="pass.
|
|
2691
|
+
task="pass.TestPassJob",
|
|
2686
2692
|
job_model=self.job_model,
|
|
2687
2693
|
interval=JobExecutionType.TYPE_FUTURE,
|
|
2688
2694
|
one_off=True,
|
|
@@ -2699,7 +2705,7 @@ class JobApprovalTest(APITestCase):
|
|
|
2699
2705
|
self.add_permissions("extras.approve_job", "extras.view_scheduledjob", "extras.change_scheduledjob")
|
|
2700
2706
|
scheduled_job = ScheduledJob.objects.create(
|
|
2701
2707
|
name="test",
|
|
2702
|
-
task="pass.
|
|
2708
|
+
task="pass.TestPassJob",
|
|
2703
2709
|
job_model=self.job_model,
|
|
2704
2710
|
interval=JobExecutionType.TYPE_FUTURE,
|
|
2705
2711
|
one_off=True,
|
|
@@ -53,12 +53,12 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
53
53
|
|
|
54
54
|
instance.refresh_from_db()
|
|
55
55
|
instance.key = "custom_field_2"
|
|
56
|
-
with self.
|
|
56
|
+
with self.assertRaisesRegex(ValidationError, "Key cannot be changed once created"):
|
|
57
57
|
instance.validated_save()
|
|
58
58
|
|
|
59
59
|
instance.refresh_from_db()
|
|
60
60
|
instance.type = CustomFieldTypeChoices.TYPE_SELECT
|
|
61
|
-
with self.
|
|
61
|
+
with self.assertRaisesRegex(ValidationError, "Type cannot be changed once created"):
|
|
62
62
|
instance.validated_save()
|
|
63
63
|
|
|
64
64
|
def test_simple_fields(self):
|
|
@@ -165,9 +165,14 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
165
165
|
cf.save()
|
|
166
166
|
cf.content_types.set([obj_type])
|
|
167
167
|
|
|
168
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option A")
|
|
169
|
-
|
|
170
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option
|
|
168
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option A", weight=100)
|
|
169
|
+
self.assertEqual(["Option A"], cf.choices)
|
|
170
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option B", weight=200)
|
|
171
|
+
self.assertEqual(["Option A", "Option B"], cf.choices)
|
|
172
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option C", weight=300)
|
|
173
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
174
|
+
with self.assertNumQueries(0): # verify caching
|
|
175
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
171
176
|
|
|
172
177
|
# Assign a value to the first Location
|
|
173
178
|
location = Location.objects.get(name="Location A")
|
|
@@ -199,9 +204,14 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
199
204
|
cf.save()
|
|
200
205
|
cf.content_types.set([obj_type])
|
|
201
206
|
|
|
202
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option A")
|
|
203
|
-
|
|
204
|
-
CustomFieldChoice.objects.create(custom_field=cf, value="Option
|
|
207
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option A", weight=100)
|
|
208
|
+
self.assertEqual(["Option A"], cf.choices)
|
|
209
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option B", weight=200)
|
|
210
|
+
self.assertEqual(["Option A", "Option B"], cf.choices)
|
|
211
|
+
CustomFieldChoice.objects.create(custom_field=cf, value="Option C", weight=300)
|
|
212
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
213
|
+
with self.assertNumQueries(0): # verify caching
|
|
214
|
+
self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
|
|
205
215
|
|
|
206
216
|
# Assign a value to the first Location
|
|
207
217
|
location = Location.objects.get(name="Location A")
|
|
@@ -293,21 +303,18 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
|
|
|
293
303
|
# Assign a disallowed value (list) to the first Location
|
|
294
304
|
location = Location.objects.get(name="Location A")
|
|
295
305
|
location.cf[cf.key] = ["I", "am", "a", "list"]
|
|
296
|
-
with self.
|
|
306
|
+
with self.assertRaisesRegex(ValidationError, "Value must be a string"):
|
|
297
307
|
location.validated_save()
|
|
298
|
-
self.assertIn("Value must be a string", str(context.exception))
|
|
299
308
|
|
|
300
309
|
# Assign another disallowed value (int) to the first Location
|
|
301
310
|
location.cf[cf.key] = 2
|
|
302
|
-
with self.
|
|
311
|
+
with self.assertRaisesRegex(ValidationError, "Value must be a string"):
|
|
303
312
|
location.validated_save()
|
|
304
|
-
self.assertIn("Value must be a string", str(context.exception))
|
|
305
313
|
|
|
306
314
|
# Assign another disallowed value (bool) to the first Location
|
|
307
315
|
location.cf[cf.key] = True
|
|
308
|
-
with self.
|
|
316
|
+
with self.assertRaisesRegex(ValidationError, "Value must be a string"):
|
|
309
317
|
location.validated_save()
|
|
310
|
-
self.assertIn("Value must be a string", str(context.exception))
|
|
311
318
|
|
|
312
319
|
# Delete the stored value
|
|
313
320
|
location.cf.pop(cf.key)
|
|
@@ -1294,7 +1301,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1294
1301
|
custom_field.content_types.set([ContentType.objects.get_for_model(Provider)])
|
|
1295
1302
|
|
|
1296
1303
|
provider = Provider.objects.create(name="Test")
|
|
1297
|
-
with self.
|
|
1304
|
+
with self.assertRaisesRegex(ValidationError, "Missing required custom field 'custom_field'"):
|
|
1298
1305
|
provider.validated_save()
|
|
1299
1306
|
|
|
1300
1307
|
def test_custom_field_required_on_update(self):
|
|
@@ -1312,7 +1319,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1312
1319
|
provider = Provider.objects.create(name="Test", _custom_field_data={"custom_field": "Value"})
|
|
1313
1320
|
provider.validated_save()
|
|
1314
1321
|
provider._custom_field_data.pop("custom_field")
|
|
1315
|
-
with self.
|
|
1322
|
+
with self.assertRaisesRegex(ValidationError, "Missing required custom field 'custom_field'"):
|
|
1316
1323
|
provider.validated_save()
|
|
1317
1324
|
|
|
1318
1325
|
def test_update_removed_custom_field(self):
|
|
@@ -1373,7 +1380,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1373
1380
|
"""
|
|
1374
1381
|
Check that a ValidationError is raised if any required custom fields are not present.
|
|
1375
1382
|
"""
|
|
1376
|
-
cf3 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Baz", required=True)
|
|
1383
|
+
cf3 = CustomField(key="baz", type=CustomFieldTypeChoices.TYPE_TEXT, label="Baz", required=True)
|
|
1377
1384
|
cf3.save()
|
|
1378
1385
|
cf3.content_types.set([ContentType.objects.get_for_model(Location)])
|
|
1379
1386
|
|
|
@@ -1381,7 +1388,7 @@ class CustomFieldModelTest(TestCase):
|
|
|
1381
1388
|
|
|
1382
1389
|
# Set custom field data with a required field omitted
|
|
1383
1390
|
location.cf["foo"] = "abc"
|
|
1384
|
-
with self.
|
|
1391
|
+
with self.assertRaisesRegex(ValidationError, "Missing required custom field 'baz'"):
|
|
1385
1392
|
location.clean()
|
|
1386
1393
|
|
|
1387
1394
|
location.cf["baz"] = "def"
|
|
@@ -1427,38 +1434,20 @@ class CustomFieldModelTest(TestCase):
|
|
|
1427
1434
|
"""
|
|
1428
1435
|
Check the GraphQL validation method on CustomField Key Attribute.
|
|
1429
1436
|
"""
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
"
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
str(error.exception),
|
|
1445
|
-
)
|
|
1446
|
-
# Check if it catches the cf.key with hyphens.
|
|
1447
|
-
cf1.key = "test-1-custom-field"
|
|
1448
|
-
with self.assertRaises(ValidationError) as error:
|
|
1449
|
-
cf1.validated_save()
|
|
1450
|
-
self.assertIn(
|
|
1451
|
-
"This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
|
|
1452
|
-
str(error.exception),
|
|
1453
|
-
)
|
|
1454
|
-
# Check if it catches the cf.key with special characters
|
|
1455
|
-
cf1.key = "test_1_custom_f)(&d"
|
|
1456
|
-
with self.assertRaises(ValidationError) as error:
|
|
1457
|
-
cf1.validated_save()
|
|
1458
|
-
self.assertIn(
|
|
1459
|
-
"This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
|
|
1460
|
-
str(error.exception),
|
|
1461
|
-
)
|
|
1437
|
+
cf1 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Test 1")
|
|
1438
|
+
for invalid_key in [
|
|
1439
|
+
"12_test_1", # Check if it catches the cf.key starting with a digit.
|
|
1440
|
+
"test 1", # Check if it catches the cf.key with whitespace.
|
|
1441
|
+
"test-1-custom-field", # Check if it catches the cf.key with hyphens.
|
|
1442
|
+
"test_1_custom_f)(&d", # Check if it catches the cf.key with special characters
|
|
1443
|
+
]:
|
|
1444
|
+
with self.assertRaisesRegex(
|
|
1445
|
+
ValidationError,
|
|
1446
|
+
"This key is not Python/GraphQL safe. "
|
|
1447
|
+
"Please do not start the key with a digit and do not use hyphens or whitespace",
|
|
1448
|
+
):
|
|
1449
|
+
cf1.key = invalid_key
|
|
1450
|
+
cf1.validated_save()
|
|
1462
1451
|
|
|
1463
1452
|
|
|
1464
1453
|
class CustomFieldFilterTest(TestCase):
|
|
@@ -1934,9 +1923,11 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
|
|
|
1934
1923
|
)
|
|
1935
1924
|
self.cf.save()
|
|
1936
1925
|
self.cf.content_types.set([obj_type])
|
|
1926
|
+
self.assertEqual(self.cf.choices, [])
|
|
1937
1927
|
|
|
1938
1928
|
self.choice = CustomFieldChoice(custom_field=self.cf, value="Foo")
|
|
1939
1929
|
self.choice.save()
|
|
1930
|
+
self.assertEqual(self.cf.choices, ["Foo"])
|
|
1940
1931
|
|
|
1941
1932
|
location_status = Status.objects.get_for_model(Location).first()
|
|
1942
1933
|
self.location_type = LocationType.objects.get(name="Campus")
|
|
@@ -1952,7 +1943,7 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
|
|
|
1952
1943
|
|
|
1953
1944
|
def test_default_value_must_be_valid_choice_sad_path(self):
|
|
1954
1945
|
self.cf.default = "invalid value"
|
|
1955
|
-
with self.
|
|
1946
|
+
with self.assertRaisesRegex(ValidationError, 'Invalid default value "invalid value"'):
|
|
1956
1947
|
self.cf.full_clean()
|
|
1957
1948
|
|
|
1958
1949
|
def test_default_value_must_be_valid_choice_happy_path(self):
|
|
@@ -1965,6 +1956,13 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
|
|
|
1965
1956
|
with self.assertRaises(ProtectedError):
|
|
1966
1957
|
self.choice.delete()
|
|
1967
1958
|
|
|
1959
|
+
def test_inactive_choice_can_be_deleted(self):
|
|
1960
|
+
self.location._custom_field_data.pop("cf1")
|
|
1961
|
+
self.location.validated_save()
|
|
1962
|
+
self.assertEqual(self.cf.choices, ["Foo"])
|
|
1963
|
+
self.choice.delete()
|
|
1964
|
+
self.assertEqual(self.cf.choices, [])
|
|
1965
|
+
|
|
1968
1966
|
def test_custom_choice_deleted_with_field(self):
|
|
1969
1967
|
self.cf.delete()
|
|
1970
1968
|
self.assertEqual(CustomField.objects.count(), 1) # custom field automatically added by the Example App
|
|
@@ -2240,7 +2238,7 @@ class CustomFieldTableTest(TestCase):
|
|
|
2240
2238
|
self.assertIsNotNone(custom_column, internal_col_name)
|
|
2241
2239
|
self.assertIsInstance(custom_column, CustomFieldColumn)
|
|
2242
2240
|
|
|
2243
|
-
rendered_value = bound_row.get_cell(internal_col_name)
|
|
2241
|
+
rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
|
|
2244
2242
|
self.assertEqual(rendered_value, col_expected_value)
|
|
2245
2243
|
|
|
2246
2244
|
|
|
@@ -30,6 +30,7 @@ from nautobot.extras.models import (
|
|
|
30
30
|
ConfigContextSchema,
|
|
31
31
|
ExportTemplate,
|
|
32
32
|
GitRepository,
|
|
33
|
+
GraphQLQuery,
|
|
33
34
|
Job,
|
|
34
35
|
JobButton,
|
|
35
36
|
JobHook,
|
|
@@ -197,6 +198,15 @@ class GitTest(TransactionTestCase):
|
|
|
197
198
|
)
|
|
198
199
|
self.assertIsNotNone(export_template_vlan)
|
|
199
200
|
|
|
201
|
+
def assert_graphql_query_exists(self, name="device_names.gql"):
|
|
202
|
+
"""Helper function to assert Graphql query exists."""
|
|
203
|
+
graphql_query = GraphQLQuery.objects.get(
|
|
204
|
+
name=name,
|
|
205
|
+
owner_object_id=self.repo.pk,
|
|
206
|
+
owner_content_type=ContentType.objects.get_for_model(GitRepository),
|
|
207
|
+
)
|
|
208
|
+
self.assertIsNotNone(graphql_query)
|
|
209
|
+
|
|
200
210
|
def assert_job_exists(self, name="MyJob", installed=True):
|
|
201
211
|
"""Helper function to assert JobModel and registered Job exist."""
|
|
202
212
|
# Is it registered correctly in the database?
|
|
@@ -285,7 +295,8 @@ class GitTest(TransactionTestCase):
|
|
|
285
295
|
|
|
286
296
|
self.repo.secrets_group = secrets_group
|
|
287
297
|
self.repo.remote_url = "http://localhost/git.git"
|
|
288
|
-
|
|
298
|
+
# avoid failing due to lack of jobs module
|
|
299
|
+
self.repo.provided_contents.remove("extras.job") # pylint: disable=no-member
|
|
289
300
|
self.repo.save()
|
|
290
301
|
|
|
291
302
|
self.mock_request.id = uuid.uuid4()
|
|
@@ -347,6 +358,10 @@ class GitTest(TransactionTestCase):
|
|
|
347
358
|
# Case when ContentType.model != ContentType.name, template was added and deleted during sync (#570)
|
|
348
359
|
self.assert_export_template_vlan_exists("template.j2")
|
|
349
360
|
|
|
361
|
+
# Make sure Graphgl queries were loaded
|
|
362
|
+
self.assert_graphql_query_exists("device_names")
|
|
363
|
+
self.assert_graphql_query_exists("device_interfaces")
|
|
364
|
+
|
|
350
365
|
# Make sure Jobs were successfully loaded from file and registered as JobModels
|
|
351
366
|
self.assert_job_exists(name="MyJob")
|
|
352
367
|
self.assert_job_exists(name="MyJobButtonReceiver")
|
|
@@ -438,6 +453,7 @@ class GitTest(TransactionTestCase):
|
|
|
438
453
|
self.assertFalse(ConfigContextSchema.objects.filter(owner_object_id=self.repo.id).exists())
|
|
439
454
|
self.assertFalse(ConfigContext.objects.filter(owner_object_id=self.repo.id).exists())
|
|
440
455
|
self.assertFalse(ExportTemplate.objects.filter(owner_object_id=self.repo.id).exists())
|
|
456
|
+
self.assertFalse(GraphQLQuery.objects.filter(owner_object_id=self.repo.id).exists())
|
|
441
457
|
self.assertFalse(Job.objects.filter(module_name__startswith=f"{self.repo.slug}.").exists())
|
|
442
458
|
device = Device.objects.get(name=self.device.name)
|
|
443
459
|
self.assertIsNone(device.local_config_context_data)
|
|
@@ -504,6 +520,11 @@ class GitTest(TransactionTestCase):
|
|
|
504
520
|
grouping="jobs",
|
|
505
521
|
message__contains="Error in loading Jobs from Git repository: ",
|
|
506
522
|
)
|
|
523
|
+
failure_logs.get(
|
|
524
|
+
grouping="graphql queries",
|
|
525
|
+
message__contains="Error processing GraphQL query file 'bad_device_names.gql': Syntax Error GraphQL (4:5) Expected Name, found }",
|
|
526
|
+
)
|
|
527
|
+
|
|
507
528
|
except (AssertionError, JobLogEntry.DoesNotExist):
|
|
508
529
|
for log in log_entries:
|
|
509
530
|
print(log.message)
|
|
@@ -629,6 +650,8 @@ class GitTest(TransactionTestCase):
|
|
|
629
650
|
self.assert_export_template_device("template.j2")
|
|
630
651
|
self.assert_export_template_html_exist("template2.html")
|
|
631
652
|
self.assert_export_template_vlan_exists("template.j2")
|
|
653
|
+
self.assert_graphql_query_exists(name="device_names")
|
|
654
|
+
self.assert_graphql_query_exists(name="device_interfaces")
|
|
632
655
|
self.assert_job_exists(name="MyJob")
|
|
633
656
|
self.assert_job_exists(name="MyJobButtonReceiver")
|
|
634
657
|
self.assert_job_exists(name="MyJobHookReceiver")
|
|
@@ -668,6 +691,8 @@ class GitTest(TransactionTestCase):
|
|
|
668
691
|
self.assert_export_template_device("template.j2")
|
|
669
692
|
self.assert_export_template_html_exist("template2.html")
|
|
670
693
|
self.assert_export_template_vlan_exists("template.j2")
|
|
694
|
+
self.assert_graphql_query_exists("device_names")
|
|
695
|
+
self.assert_graphql_query_exists("device_interfaces")
|
|
671
696
|
self.assert_job_exists(name="MyJob")
|
|
672
697
|
self.assert_job_exists(name="MyJobButtonReceiver")
|
|
673
698
|
self.assert_job_exists(name="MyJobHookReceiver")
|
|
@@ -710,6 +735,8 @@ class GitTest(TransactionTestCase):
|
|
|
710
735
|
log_entries.get(message__contains="Addition - `export_templates/dcim/device/template.j2`")
|
|
711
736
|
log_entries.get(message__contains="Addition - `export_templates/dcim/device/template2.html`")
|
|
712
737
|
log_entries.get(message__contains="Addition - `export_templates/ipam/vlan/template.j2`")
|
|
738
|
+
log_entries.get(message__contains="Addition - `graphql_queries/device_interfaces.gql`")
|
|
739
|
+
log_entries.get(message__contains="Addition - `graphql_queries/device_names.gql`")
|
|
713
740
|
log_entries.get(message__contains="Addition - `jobs/__init__.py`")
|
|
714
741
|
log_entries.get(message__contains="Addition - `jobs/my_job.py`")
|
|
715
742
|
except JobLogEntry.DoesNotExist:
|
|
@@ -720,6 +747,7 @@ class GitTest(TransactionTestCase):
|
|
|
720
747
|
self.assertFalse(ConfigContextSchema.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
721
748
|
self.assertFalse(ConfigContext.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
722
749
|
self.assertFalse(ExportTemplate.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
750
|
+
self.assertFalse(GraphQLQuery.objects.filter(owner_object_id=self.repo.pk).exists())
|
|
723
751
|
self.assertFalse(Job.objects.filter(module_name__startswith=self.repo.slug).exists())
|
|
724
752
|
|
|
725
753
|
# TODO: test dry-run against a branch name
|
|
@@ -587,7 +587,7 @@ class DynamicGroupModelTest(DynamicGroupTestBase): # TODO: BaseModelTestCase mi
|
|
|
587
587
|
"""
|
|
588
588
|
pfx_content_type = ContentType.objects.get_for_model(Prefix)
|
|
589
589
|
group = DynamicGroup(name="pfx", content_type=pfx_content_type)
|
|
590
|
-
filterset = group.filterset_class()
|
|
590
|
+
filterset = group.filterset_class() # pylint: disable=not-callable # should not be None here!
|
|
591
591
|
fields = group._map_filter_fields
|
|
592
592
|
|
|
593
593
|
# We know that `within_include` does not have a `generate_query_{filter_method}` method.
|
|
@@ -890,11 +890,11 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
890
890
|
Job.objects.last().tags.set(Tag.objects.get_for_model(Job)[:3])
|
|
891
891
|
|
|
892
892
|
def test_installed(self):
|
|
893
|
-
params = {"job_class_name": "
|
|
893
|
+
params = {"job_class_name": "TestPassJob", "installed": True}
|
|
894
894
|
self.assertTrue(self.filterset(params, self.queryset).qs.exists())
|
|
895
895
|
|
|
896
896
|
def test_enabled(self):
|
|
897
|
-
params = {"job_class_name": "
|
|
897
|
+
params = {"job_class_name": "TestPassJob", "enabled": False}
|
|
898
898
|
self.assertTrue(self.filterset(params, self.queryset).qs.exists())
|
|
899
899
|
|
|
900
900
|
def test_dryrun_default(self):
|
|
@@ -992,11 +992,11 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
992
992
|
jobs = Job.objects.all()[:3]
|
|
993
993
|
cls.jobs = jobs
|
|
994
994
|
user = User.objects.create(username="user1", is_active=True)
|
|
995
|
-
job_model = Job.objects.get_for_class_path("pass.
|
|
995
|
+
job_model = Job.objects.get_for_class_path("pass.TestPassJob")
|
|
996
996
|
scheduled_jobs = [
|
|
997
997
|
ScheduledJob.objects.create(
|
|
998
998
|
name="test1",
|
|
999
|
-
task="pass.
|
|
999
|
+
task="pass.TestPassJob",
|
|
1000
1000
|
job_model=job_model,
|
|
1001
1001
|
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1002
1002
|
user=user,
|
|
@@ -1005,7 +1005,7 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
1005
1005
|
),
|
|
1006
1006
|
ScheduledJob.objects.create(
|
|
1007
1007
|
name="test2",
|
|
1008
|
-
task="pass.
|
|
1008
|
+
task="pass.TestPassJob",
|
|
1009
1009
|
job_model=job_model,
|
|
1010
1010
|
interval=JobExecutionType.TYPE_DAILY,
|
|
1011
1011
|
user=user,
|
|
@@ -1015,7 +1015,7 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
|
|
|
1015
1015
|
),
|
|
1016
1016
|
ScheduledJob.objects.create(
|
|
1017
1017
|
name="test3",
|
|
1018
|
-
task="pass.
|
|
1018
|
+
task="pass.TestPassJob",
|
|
1019
1019
|
job_model=job_model,
|
|
1020
1020
|
interval=JobExecutionType.TYPE_CUSTOM,
|
|
1021
1021
|
crontab="34 12 * * *",
|
|
@@ -4,12 +4,13 @@ import warnings
|
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
5
|
from django.contrib.contenttypes.models import ContentType
|
|
6
6
|
from django.db.models import Q
|
|
7
|
+
from django.forms import ChoiceField, MultipleChoiceField
|
|
7
8
|
from django.test import override_settings, TestCase
|
|
8
9
|
|
|
9
10
|
from nautobot.dcim.forms import DeviceForm, LocationBulkEditForm, LocationForm
|
|
10
11
|
import nautobot.dcim.models as dcim_models
|
|
11
12
|
from nautobot.dcim.models import Device, Location, LocationType
|
|
12
|
-
from nautobot.extras.choices import RelationshipTypeChoices
|
|
13
|
+
from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoices
|
|
13
14
|
from nautobot.extras.forms import (
|
|
14
15
|
ConfigContextFilterForm,
|
|
15
16
|
ConfigContextForm,
|
|
@@ -25,6 +26,7 @@ from nautobot.extras.forms import (
|
|
|
25
26
|
WebhookForm,
|
|
26
27
|
)
|
|
27
28
|
from nautobot.extras.models import (
|
|
29
|
+
CustomField,
|
|
28
30
|
Job,
|
|
29
31
|
JobButton,
|
|
30
32
|
JobHook,
|
|
@@ -908,6 +910,19 @@ class RelationshipModelBulkEditFormMixinTestCase(TestCase):
|
|
|
908
910
|
ras = RelationshipAssociation.objects.filter(relationship=self.rel_mtom_s)
|
|
909
911
|
self.assertEqual(1, ras.count())
|
|
910
912
|
|
|
913
|
+
def test_form_save_relationship_with_nullified_fields_is_none(self):
|
|
914
|
+
"""Test save_relationships with nullified_fields=None."""
|
|
915
|
+
form = LocationBulkEditForm(
|
|
916
|
+
model=dcim_models.Location,
|
|
917
|
+
data={
|
|
918
|
+
"pks": [self.locations[0].pk],
|
|
919
|
+
"add_cr_multiplexing__destination": [ipaddress.pk for ipaddress in self.ipaddresses],
|
|
920
|
+
"add_cr_peer_locations__peer": [self.locations[1].pk],
|
|
921
|
+
},
|
|
922
|
+
)
|
|
923
|
+
form.is_valid()
|
|
924
|
+
form.save_relationships(instance=self.locations[0], nullified_fields=None)
|
|
925
|
+
|
|
911
926
|
def test_location_form_remove_mtom(self):
|
|
912
927
|
"""Test removal of relationship-associations for many-to-many relationships."""
|
|
913
928
|
RelationshipAssociation.objects.create(
|
|
@@ -1217,3 +1232,20 @@ class CustomFieldModelFormMixinTestCase(TestCase):
|
|
|
1217
1232
|
|
|
1218
1233
|
custom_field_form = TestForm()
|
|
1219
1234
|
self.assertIn("_custom_field_data", custom_field_form.fields)
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
class CustomFieldTestCase(TestCase):
|
|
1238
|
+
def test_to_form_field_type_select(self):
|
|
1239
|
+
"""Verify that `to_form_field` and `to_filter_form_field` return the correct field types for a select-type CustomField."""
|
|
1240
|
+
custom_field = CustomField.objects.create(
|
|
1241
|
+
type=CustomFieldTypeChoices.TYPE_SELECT,
|
|
1242
|
+
label="Custom Field Select",
|
|
1243
|
+
)
|
|
1244
|
+
form = custom_field.to_filter_form_field()
|
|
1245
|
+
self.assertIsInstance(form, MultipleChoiceField)
|
|
1246
|
+
|
|
1247
|
+
form = custom_field.to_form_field(for_filter_form=True)
|
|
1248
|
+
self.assertIsInstance(form, MultipleChoiceField)
|
|
1249
|
+
|
|
1250
|
+
form = custom_field.to_form_field(for_filter_form=False)
|
|
1251
|
+
self.assertIsInstance(form, ChoiceField)
|