nautobot 2.3.16__py3-none-any.whl → 2.4.0__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/__init__.py +15 -0
- nautobot/apps/__init__.py +1 -1
- nautobot/apps/api.py +8 -10
- nautobot/apps/change_logging.py +2 -2
- nautobot/apps/choices.py +4 -4
- nautobot/apps/config.py +32 -3
- nautobot/apps/events.py +19 -0
- nautobot/apps/exceptions.py +0 -2
- 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 +44 -9
- nautobot/apps/utils.py +7 -15
- nautobot/apps/views.py +8 -6
- nautobot/circuits/api/serializers.py +1 -0
- nautobot/circuits/api/views.py +4 -8
- nautobot/circuits/navigation.py +0 -57
- nautobot/circuits/templates/circuits/circuit_create.html +1 -7
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
- nautobot/circuits/tests/integration/test_relationships.py +33 -24
- nautobot/circuits/tests/test_filters.py +4 -8
- nautobot/circuits/views.py +140 -23
- nautobot/cloud/api/views.py +6 -10
- nautobot/cloud/factory.py +4 -1
- nautobot/cloud/tests/test_filters.py +5 -4
- nautobot/cloud/views.py +0 -16
- nautobot/core/api/constants.py +11 -0
- nautobot/core/api/filter_backends.py +3 -9
- nautobot/core/api/metadata.py +28 -256
- nautobot/core/api/pagination.py +3 -2
- nautobot/core/api/renderers.py +3 -0
- nautobot/core/api/schema.py +13 -2
- nautobot/core/api/serializers.py +45 -259
- nautobot/core/api/urls.py +3 -4
- nautobot/core/api/utils.py +0 -62
- nautobot/core/api/views.py +99 -157
- nautobot/core/apps/__init__.py +22 -578
- nautobot/core/celery/__init__.py +13 -0
- nautobot/core/celery/schedulers.py +47 -2
- nautobot/core/choices.py +2 -2
- nautobot/core/cli/__init__.py +8 -0
- nautobot/core/constants.py +7 -0
- nautobot/core/events/__init__.py +116 -0
- nautobot/core/events/base.py +27 -0
- nautobot/core/events/exceptions.py +10 -0
- nautobot/core/events/redis_broker.py +48 -0
- nautobot/core/events/syslog_broker.py +19 -0
- nautobot/core/exceptions.py +0 -6
- nautobot/core/forms/__init__.py +19 -19
- nautobot/core/forms/fields.py +57 -9
- nautobot/core/forms/forms.py +33 -2
- nautobot/core/forms/utils.py +2 -1
- nautobot/core/graphql/schema.py +3 -1
- nautobot/core/jobs/__init__.py +24 -3
- nautobot/core/jobs/bulk_actions.py +248 -0
- nautobot/core/jobs/cleanup.py +1 -1
- nautobot/core/management/commands/generate_test_data.py +21 -0
- nautobot/core/middleware.py +16 -0
- nautobot/core/models/fields.py +11 -7
- nautobot/core/settings.py +68 -4
- nautobot/core/settings.yaml +99 -0
- nautobot/core/tables.py +10 -46
- nautobot/core/tasks.py +1 -1
- nautobot/core/templates/about.html +67 -0
- nautobot/core/templates/components/button/default.html +7 -0
- nautobot/core/templates/components/button/dropdown.html +20 -0
- nautobot/core/templates/components/layout/one_over_two.html +19 -0
- nautobot/core/templates/components/layout/two_over_one.html +19 -0
- nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
- nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
- nautobot/core/templates/components/panel/body_content_tags.html +6 -0
- nautobot/core/templates/components/panel/body_content_text.html +12 -0
- nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
- nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
- nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
- nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
- nautobot/core/templates/components/panel/footer_content_table.html +14 -0
- nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
- nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
- nautobot/core/templates/components/panel/panel.html +16 -0
- nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
- nautobot/core/templates/components/tab/content_wrapper.html +3 -0
- nautobot/core/templates/components/tab/label_wrapper.html +5 -0
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
- nautobot/core/templates/generic/object_retrieve.html +28 -17
- nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
- nautobot/core/templates/inc/custom_fields/panel.html +2 -2
- nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
- nautobot/core/templates/inc/footer.html +1 -0
- nautobot/core/templates/inc/nav_menu.html +2 -1
- nautobot/core/templates/inc/relationships_panel.html +1 -1
- 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/panel_table.html +12 -0
- nautobot/core/templates/utilities/render_jinja2.html +117 -0
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/theme_preview.html +7 -0
- nautobot/core/templatetags/helpers.py +104 -6
- nautobot/core/templatetags/ui_framework.py +40 -0
- nautobot/core/testing/__init__.py +8 -8
- nautobot/core/testing/api.py +187 -137
- nautobot/core/testing/context.py +18 -0
- nautobot/core/testing/filters.py +41 -35
- nautobot/core/testing/forms.py +2 -0
- nautobot/core/testing/views.py +65 -148
- nautobot/core/tests/integration/test_view_authentication.py +1 -1
- nautobot/core/tests/nautobot_config.py +198 -0
- nautobot/core/tests/runner.py +2 -2
- nautobot/core/tests/test_api.py +154 -176
- nautobot/core/tests/test_events.py +214 -0
- nautobot/core/tests/test_forms.py +1 -0
- nautobot/core/tests/test_jinja_filters.py +1 -0
- nautobot/core/tests/test_jobs.py +387 -14
- nautobot/core/tests/test_navigations.py +7 -241
- nautobot/core/tests/test_settings_schema.py +7 -0
- nautobot/core/tests/test_tables.py +100 -0
- nautobot/core/tests/test_templatetags_helpers.py +16 -0
- nautobot/core/tests/test_ui.py +150 -0
- nautobot/core/tests/test_utils.py +55 -18
- nautobot/core/tests/test_views.py +124 -5
- nautobot/core/ui/__init__.py +0 -0
- nautobot/core/ui/base.py +11 -0
- nautobot/core/ui/choices.py +44 -0
- nautobot/core/ui/homepage.py +167 -0
- nautobot/core/ui/nav.py +280 -0
- nautobot/core/ui/object_detail.py +1855 -0
- nautobot/core/ui/utils.py +36 -0
- nautobot/core/urls.py +6 -0
- nautobot/core/utils/config.py +30 -3
- nautobot/core/utils/lookup.py +12 -2
- nautobot/core/utils/querysets.py +64 -0
- nautobot/core/utils/requests.py +24 -9
- nautobot/core/views/__init__.py +48 -1
- nautobot/core/views/generic.py +37 -140
- nautobot/core/views/mixins.py +82 -32
- nautobot/core/views/paginator.py +8 -5
- nautobot/core/views/renderers.py +9 -9
- nautobot/core/views/utils.py +11 -0
- nautobot/core/wsgi.py +3 -3
- nautobot/dcim/api/serializers.py +34 -141
- nautobot/dcim/api/urls.py +5 -0
- nautobot/dcim/api/views.py +57 -110
- nautobot/dcim/apps.py +1 -0
- nautobot/dcim/choices.py +28 -0
- nautobot/dcim/factory.py +58 -0
- nautobot/dcim/filters/__init__.py +204 -2
- nautobot/dcim/forms.py +219 -9
- nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
- nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
- nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
- nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
- nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
- nautobot/dcim/models/__init__.py +5 -1
- nautobot/dcim/models/devices.py +180 -2
- nautobot/dcim/models/racks.py +2 -2
- nautobot/dcim/navigation.py +25 -224
- nautobot/dcim/signals.py +44 -0
- nautobot/dcim/tables/__init__.py +2 -0
- nautobot/dcim/tables/devices.py +103 -7
- nautobot/dcim/tables/racks.py +1 -1
- nautobot/dcim/templates/dcim/controller/base.html +10 -0
- nautobot/dcim/templates/dcim/controller_create.html +2 -7
- nautobot/dcim/templates/dcim/controller_retrieve.html +6 -10
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +51 -0
- nautobot/dcim/templates/dcim/device/base.html +6 -42
- nautobot/dcim/templates/dcim/device/wireless.html +73 -0
- nautobot/dcim/templates/dcim/device.html +4 -10
- nautobot/dcim/templates/dcim/device_edit.html +36 -37
- nautobot/dcim/templates/dcim/interface.html +1 -0
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/location.html +1 -9
- nautobot/dcim/templates/dcim/location_edit.html +1 -7
- nautobot/dcim/templates/dcim/locationtype.html +0 -107
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
- nautobot/dcim/templates/dcim/rack.html +1 -9
- nautobot/dcim/templates/dcim/rack_edit.html +1 -7
- nautobot/dcim/templates/dcim/rackreservation.html +1 -9
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +68 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +28 -0
- nautobot/dcim/tests/integration/test_controller.py +62 -0
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
- nautobot/dcim/tests/test_api.py +188 -64
- nautobot/dcim/tests/test_filters.py +171 -76
- nautobot/dcim/tests/test_jobs.py +118 -0
- nautobot/dcim/tests/test_models.py +157 -6
- nautobot/dcim/tests/test_signals.py +1 -0
- nautobot/dcim/tests/test_views.py +118 -88
- nautobot/dcim/urls.py +72 -27
- nautobot/dcim/utils.py +2 -2
- nautobot/dcim/views.py +356 -61
- nautobot/extras/api/serializers.py +39 -18
- nautobot/extras/api/urls.py +4 -0
- nautobot/extras/api/views.py +89 -31
- nautobot/extras/choices.py +13 -0
- nautobot/extras/constants.py +2 -1
- nautobot/extras/context_managers.py +23 -6
- nautobot/extras/datasources/git.py +4 -1
- nautobot/extras/factory.py +27 -0
- nautobot/extras/filters/__init__.py +66 -5
- nautobot/extras/forms/base.py +2 -2
- nautobot/extras/forms/forms.py +262 -59
- nautobot/extras/forms/mixins.py +2 -2
- nautobot/extras/graphql/types.py +25 -1
- nautobot/extras/jobs.py +109 -15
- nautobot/extras/management/__init__.py +1 -0
- nautobot/extras/management/commands/runjob.py +7 -79
- nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
- nautobot/extras/management/utils.py +87 -0
- nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
- nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
- nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
- nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
- nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
- nautobot/extras/models/__init__.py +5 -1
- nautobot/extras/models/change_logging.py +7 -3
- nautobot/extras/models/contacts.py +1 -1
- nautobot/extras/models/groups.py +0 -2
- nautobot/extras/models/jobs.py +233 -33
- nautobot/extras/models/relationships.py +69 -1
- nautobot/extras/models/secrets.py +5 -0
- nautobot/extras/navigation.py +20 -262
- nautobot/extras/plugins/__init__.py +54 -19
- nautobot/extras/plugins/marketplace_manifest.yml +455 -0
- nautobot/extras/plugins/tables.py +16 -14
- nautobot/extras/plugins/urls.py +1 -0
- nautobot/extras/plugins/views.py +103 -60
- nautobot/extras/registry.py +1 -1
- nautobot/extras/secrets/__init__.py +2 -2
- nautobot/extras/signals.py +39 -1
- nautobot/extras/tables.py +37 -1
- nautobot/extras/templates/extras/dynamicgroup.html +1 -9
- nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
- nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
- nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
- nautobot/extras/templates/extras/job_detail.html +52 -6
- nautobot/extras/templates/extras/job_edit.html +6 -2
- nautobot/extras/templates/extras/job_list.html +2 -7
- nautobot/extras/templates/extras/jobqueue_retrieve.html +36 -0
- nautobot/extras/templates/extras/marketplace.html +296 -0
- nautobot/extras/templates/extras/plugin_detail.html +32 -15
- nautobot/extras/templates/extras/plugins_list.html +35 -1
- nautobot/extras/templates/extras/plugins_tiles.html +90 -0
- nautobot/extras/templates/extras/role_retrieve.html +16 -0
- nautobot/extras/templates/extras/secret.html +0 -65
- nautobot/extras/templates/extras/secret_check.js +16 -0
- nautobot/extras/templates/extras/secret_create.html +114 -0
- nautobot/extras/templates/extras/secret_edit.html +1 -114
- nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
- nautobot/extras/templatetags/job_buttons.py +5 -4
- nautobot/extras/templatetags/plugins.py +69 -6
- nautobot/extras/test_jobs/singleton.py +16 -0
- nautobot/extras/tests/test_api.py +145 -43
- nautobot/extras/tests/test_context_managers.py +4 -1
- nautobot/extras/tests/test_filters.py +213 -529
- nautobot/extras/tests/test_job_variables.py +73 -152
- nautobot/extras/tests/test_jobs.py +181 -51
- nautobot/extras/tests/test_models.py +61 -6
- nautobot/extras/tests/test_plugins.py +62 -9
- nautobot/extras/tests/test_relationships.py +123 -9
- nautobot/extras/tests/test_utils.py +23 -2
- nautobot/extras/tests/test_views.py +146 -145
- nautobot/extras/tests/test_webhooks.py +2 -1
- nautobot/extras/urls.py +2 -20
- nautobot/extras/utils.py +119 -4
- nautobot/extras/views.py +168 -125
- nautobot/extras/webhooks.py +5 -2
- nautobot/ipam/api/serializers.py +10 -103
- nautobot/ipam/api/views.py +31 -49
- nautobot/ipam/factory.py +1 -1
- nautobot/ipam/filters.py +3 -2
- nautobot/ipam/models.py +10 -12
- nautobot/ipam/navigation.py +0 -90
- nautobot/ipam/tables.py +3 -1
- nautobot/ipam/templates/ipam/ipaddress.html +1 -9
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
- nautobot/ipam/templates/ipam/prefix.html +1 -9
- nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
- nautobot/ipam/templates/ipam/routetarget.html +0 -28
- nautobot/ipam/templates/ipam/vlan.html +1 -9
- nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
- nautobot/ipam/templates/ipam/vrf.html +0 -47
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
- nautobot/ipam/tests/test_api.py +7 -5
- nautobot/ipam/tests/test_filters.py +39 -119
- nautobot/ipam/tests/test_forms.py +0 -2
- nautobot/ipam/tests/test_models.py +56 -36
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/ipam/urls.py +3 -69
- nautobot/ipam/utils/__init__.py +16 -10
- nautobot/ipam/views.py +91 -162
- 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/css/base.css +38 -3
- nautobot/project-static/docs/404.html +461 -17
- nautobot/project-static/docs/apps/index.html +461 -17
- nautobot/project-static/docs/apps/nautobot-apps.html +462 -19
- nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
- nautobot/project-static/docs/assets/extra.css +5 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +477 -23
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +474 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +790 -289
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +505 -31
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +510 -34
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +471 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +467 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +497 -33
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9883 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +523 -75
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +546 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +670 -94
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1030 -177
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +524 -49
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +874 -188
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +955 -235
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +475 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +486 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +661 -99
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +947 -479
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6425 -1234
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +474 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +877 -344
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +828 -171
- nautobot/project-static/docs/development/apps/api/configuration-view.html +461 -17
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/global-search.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/graphql.html +461 -17
- nautobot/project-static/docs/development/apps/api/models/index.html +461 -17
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +461 -17
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +461 -17
- nautobot/project-static/docs/development/apps/api/prometheus.html +461 -17
- nautobot/project-static/docs/development/apps/api/setup.html +465 -153
- nautobot/project-static/docs/development/apps/api/testing.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +461 -17
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +741 -128
- nautobot/project-static/docs/development/apps/api/views/base-template.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/index.html +463 -18
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +465 -17
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +491 -17
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/notes.html +461 -17
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +467 -19
- nautobot/project-static/docs/development/apps/api/views/urls.html +461 -17
- nautobot/project-static/docs/development/apps/index.html +461 -17
- nautobot/project-static/docs/development/apps/migration/code-updates.html +462 -50
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +462 -18
- nautobot/project-static/docs/development/apps/migration/from-v1.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +461 -17
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +464 -20
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9261 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9375 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9671 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9559 -0
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +464 -20
- nautobot/project-static/docs/development/core/application-registry.html +461 -17
- nautobot/project-static/docs/development/core/best-practices.html +461 -17
- nautobot/project-static/docs/development/core/bootstrap-ui.html +461 -17
- nautobot/project-static/docs/development/core/caching.html +461 -17
- nautobot/project-static/docs/development/core/controllers.html +463 -17
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +464 -20
- nautobot/project-static/docs/development/core/generic-views.html +461 -17
- nautobot/project-static/docs/development/core/getting-started.html +539 -127
- nautobot/project-static/docs/development/core/homepage.html +472 -28
- nautobot/project-static/docs/development/core/index.html +461 -17
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9754 -0
- nautobot/project-static/docs/development/core/model-checklist.html +471 -25
- nautobot/project-static/docs/development/core/model-features.html +461 -17
- nautobot/project-static/docs/development/core/natural-keys.html +461 -17
- nautobot/project-static/docs/development/core/navigation-menu.html +478 -24
- nautobot/project-static/docs/development/core/release-checklist.html +478 -46
- nautobot/project-static/docs/development/core/role-internals.html +461 -17
- nautobot/project-static/docs/development/core/settings.html +461 -17
- nautobot/project-static/docs/development/core/style-guide.html +464 -20
- nautobot/project-static/docs/development/core/templates.html +471 -20
- nautobot/project-static/docs/development/core/testing.html +461 -17
- nautobot/project-static/docs/development/core/ui-component-framework.html +11116 -0
- nautobot/project-static/docs/development/core/user-preferences.html +464 -20
- nautobot/project-static/docs/development/index.html +461 -17
- nautobot/project-static/docs/development/jobs/index.html +499 -19
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +461 -17
- nautobot/project-static/docs/index.html +469 -36
- 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/basic-panel-layout.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.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/dropdown-button-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-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/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
- nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
- nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
- nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
- nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
- nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
- nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
- nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +467 -21
- nautobot/project-static/docs/overview/design_philosophy.html +461 -17
- nautobot/project-static/docs/release-notes/index.html +483 -20
- nautobot/project-static/docs/release-notes/version-1.0.html +649 -206
- nautobot/project-static/docs/release-notes/version-1.1.html +646 -203
- nautobot/project-static/docs/release-notes/version-1.2.html +721 -278
- nautobot/project-static/docs/release-notes/version-1.3.html +747 -304
- nautobot/project-static/docs/release-notes/version-1.4.html +832 -390
- nautobot/project-static/docs/release-notes/version-1.5.html +1020 -579
- nautobot/project-static/docs/release-notes/version-1.6.html +940 -516
- nautobot/project-static/docs/release-notes/version-2.0.html +943 -502
- nautobot/project-static/docs/release-notes/version-2.1.html +778 -337
- nautobot/project-static/docs/release-notes/version-2.2.html +771 -330
- nautobot/project-static/docs/release-notes/version-2.3.html +914 -471
- nautobot/project-static/docs/release-notes/version-2.4.html +10323 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +342 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +473 -30
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +461 -17
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +842 -155
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +474 -27
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +461 -17
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +463 -19
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +481 -21
- nautobot/project-static/docs/user-guide/administration/installation/index.html +466 -18
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +462 -18
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +461 -17
- nautobot/project-static/docs/user-guide/administration/installation/services.html +461 -17
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +461 -17
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +482 -39
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +475 -64
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +475 -64
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +464 -21
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +461 -17
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +467 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +497 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +487 -20
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9375 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +468 -28
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +464 -20
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +461 -17
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +464 -20
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9313 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9217 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9211 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9277 -0
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +466 -20
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +461 -17
- 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 +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +461 -17
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +464 -20
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
- nautobot/project-static/docs/user-guide/index.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +465 -21
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +9617 -0
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +470 -21
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9224 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +465 -21
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9292 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +509 -38
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +492 -21
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +464 -20
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +529 -54
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +461 -17
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +461 -17
- nautobot/project-static/img/jinja_logo.svg +97 -0
- nautobot/project-static/js/forms.js +6 -1
- nautobot/project-static/js/nav_menu.js +2 -1
- nautobot/tenancy/api/serializers.py +0 -2
- nautobot/tenancy/api/views.py +9 -13
- nautobot/tenancy/factory.py +1 -1
- nautobot/tenancy/navigation.py +0 -29
- nautobot/tenancy/templates/tenancy/tenant.html +4 -91
- nautobot/tenancy/tests/test_filters.py +29 -134
- nautobot/tenancy/views.py +35 -24
- nautobot/users/admin.py +2 -0
- nautobot/users/api/views.py +2 -2
- nautobot/users/forms.py +19 -0
- nautobot/users/templates/users/preferences.html +22 -0
- nautobot/users/tests/test_filters.py +1 -19
- nautobot/users/tests/test_views.py +57 -0
- nautobot/users/utils.py +8 -0
- nautobot/users/views.py +48 -11
- nautobot/virtualization/api/views.py +5 -24
- nautobot/virtualization/filters.py +1 -2
- nautobot/virtualization/models.py +1 -1
- nautobot/virtualization/navigation.py +0 -48
- nautobot/virtualization/tables.py +2 -2
- nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
- nautobot/virtualization/tests/test_filters.py +57 -166
- nautobot/virtualization/views.py +18 -15
- nautobot/wireless/__init__.py +0 -0
- nautobot/wireless/api/__init__.py +0 -0
- nautobot/wireless/api/serializers.py +44 -0
- nautobot/wireless/api/urls.py +20 -0
- nautobot/wireless/api/views.py +34 -0
- nautobot/wireless/apps.py +8 -0
- nautobot/wireless/choices.py +345 -0
- nautobot/wireless/factory.py +138 -0
- nautobot/wireless/filters.py +167 -0
- nautobot/wireless/forms.py +283 -0
- nautobot/wireless/homepage.py +19 -0
- nautobot/wireless/migrations/0001_initial.py +223 -0
- nautobot/wireless/migrations/__init__.py +0 -0
- nautobot/wireless/models.py +207 -0
- nautobot/wireless/navigation.py +105 -0
- nautobot/wireless/tables.py +244 -0
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +56 -0
- nautobot/wireless/tests/__init__.py +0 -0
- nautobot/wireless/tests/integration/__init__.py +0 -0
- nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
- nautobot/wireless/tests/test_api.py +247 -0
- nautobot/wireless/tests/test_filters.py +82 -0
- nautobot/wireless/tests/test_models.py +22 -0
- nautobot/wireless/tests/test_views.py +378 -0
- nautobot/wireless/urls.py +13 -0
- nautobot/wireless/views.py +119 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/METADATA +9 -12
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/RECORD +720 -549
- nautobot/core/utils/navigation.py +0 -54
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/NOTICE +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/WHEEL +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/entry_points.txt +0 -0
nautobot/circuits/views.py
CHANGED
|
@@ -2,9 +2,18 @@ from django.contrib import messages
|
|
|
2
2
|
from django.db import transaction
|
|
3
3
|
from django.db.models import Q
|
|
4
4
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
5
|
+
from django.utils.html import format_html
|
|
5
6
|
from django_tables2 import RequestConfig
|
|
6
7
|
|
|
7
8
|
from nautobot.core.forms import ConfirmationForm
|
|
9
|
+
from nautobot.core.templatetags.helpers import bettertitle, humanize_speed, placeholder
|
|
10
|
+
from nautobot.core.ui.choices import SectionChoices
|
|
11
|
+
from nautobot.core.ui.object_detail import (
|
|
12
|
+
ObjectDetailContent,
|
|
13
|
+
ObjectFieldsPanel,
|
|
14
|
+
ObjectsTablePanel,
|
|
15
|
+
)
|
|
16
|
+
from nautobot.core.ui.utils import render_component_template
|
|
8
17
|
from nautobot.core.views import generic, mixins as view_mixins
|
|
9
18
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
10
19
|
from nautobot.core.views.viewsets import NautobotUIViewSet
|
|
@@ -94,27 +103,22 @@ class ProviderUIViewSet(NautobotUIViewSet):
|
|
|
94
103
|
queryset = Provider.objects.all()
|
|
95
104
|
serializer_class = serializers.ProviderSerializer
|
|
96
105
|
table_class = tables.ProviderTable
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
RequestConfig(request, paginate).configure(circuits_table)
|
|
115
|
-
|
|
116
|
-
context["circuits_table"] = circuits_table
|
|
117
|
-
return context
|
|
106
|
+
object_detail_content = ObjectDetailContent(
|
|
107
|
+
panels=(
|
|
108
|
+
ObjectFieldsPanel(
|
|
109
|
+
section=SectionChoices.LEFT_HALF,
|
|
110
|
+
weight=100,
|
|
111
|
+
fields="__all__",
|
|
112
|
+
),
|
|
113
|
+
ObjectsTablePanel(
|
|
114
|
+
weight=200,
|
|
115
|
+
table_class=tables.CircuitTable,
|
|
116
|
+
table_filter="provider",
|
|
117
|
+
section=SectionChoices.FULL_WIDTH,
|
|
118
|
+
exclude_columns=["provider"],
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
)
|
|
118
122
|
|
|
119
123
|
|
|
120
124
|
class CircuitUIViewSet(NautobotUIViewSet):
|
|
@@ -127,8 +131,121 @@ class CircuitUIViewSet(NautobotUIViewSet):
|
|
|
127
131
|
queryset = Circuit.objects.all()
|
|
128
132
|
serializer_class = serializers.CircuitSerializer
|
|
129
133
|
table_class = tables.CircuitTable
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
|
|
135
|
+
class CircuitTerminationPanel(ObjectFieldsPanel):
|
|
136
|
+
def __init__(self, **kwargs):
|
|
137
|
+
super().__init__(
|
|
138
|
+
fields=(
|
|
139
|
+
"location", # TODO: render location hierarchy
|
|
140
|
+
"cable",
|
|
141
|
+
"provider_network",
|
|
142
|
+
"cloud_network",
|
|
143
|
+
"port_speed",
|
|
144
|
+
"upstream_speed",
|
|
145
|
+
"ip_addresses",
|
|
146
|
+
"xconnect_id",
|
|
147
|
+
"pp_info",
|
|
148
|
+
"description",
|
|
149
|
+
),
|
|
150
|
+
value_transforms={
|
|
151
|
+
"port_speed": [humanize_speed, placeholder],
|
|
152
|
+
"upstream_speed": [humanize_speed],
|
|
153
|
+
},
|
|
154
|
+
hide_if_unset=("location", "provider_network", "cloud_network", "upstream_speed"),
|
|
155
|
+
ignore_nonexistent_fields=True, # ip_addresses may be undefined
|
|
156
|
+
header_extra_content_template_path="circuits/inc/circuit_termination_header_extra_content.html",
|
|
157
|
+
**kwargs,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def should_render(self, context):
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
def get_extra_context(self, context):
|
|
164
|
+
return {"termination": context[self.context_object_key]}
|
|
165
|
+
|
|
166
|
+
def get_data(self, context):
|
|
167
|
+
"""
|
|
168
|
+
Extend the panel data to include custom relationships on the termination.
|
|
169
|
+
|
|
170
|
+
This is done for feature parity with the existing UI, and is not a pattern we *generally* should emulate.
|
|
171
|
+
"""
|
|
172
|
+
data = super().get_data(context)
|
|
173
|
+
termination = context["termination"]
|
|
174
|
+
if termination is not None:
|
|
175
|
+
for side, relationships in termination.get_relationships_with_related_objects(
|
|
176
|
+
include_hidden=False
|
|
177
|
+
).items():
|
|
178
|
+
for relationship, value in relationships.items():
|
|
179
|
+
key = (relationship, side)
|
|
180
|
+
data[key] = value
|
|
181
|
+
|
|
182
|
+
return data
|
|
183
|
+
|
|
184
|
+
def render_key(self, key, value, context):
|
|
185
|
+
"""
|
|
186
|
+
Extend the panel rendering to render custom relationship information.
|
|
187
|
+
|
|
188
|
+
This is done for feature parity with the existing UI, and is not a pattern we *generally* should emulate.
|
|
189
|
+
"""
|
|
190
|
+
if isinstance(key, tuple):
|
|
191
|
+
# Copied from _ObjectRelationshipsPanel.render_key()
|
|
192
|
+
relationship, side = key
|
|
193
|
+
return format_html(
|
|
194
|
+
'<span title="{} ({})">{}</span>',
|
|
195
|
+
relationship.label,
|
|
196
|
+
relationship.key,
|
|
197
|
+
bettertitle(relationship.get_label(side)),
|
|
198
|
+
)
|
|
199
|
+
return super().render_key(key, value, context)
|
|
200
|
+
|
|
201
|
+
def render_value(self, key, value, context):
|
|
202
|
+
"""
|
|
203
|
+
Add custom rendering of connected cables.
|
|
204
|
+
|
|
205
|
+
TODO: this might make sense to move into the base class to handle Cable objects in general?
|
|
206
|
+
"""
|
|
207
|
+
if key == "cable":
|
|
208
|
+
if not context["termination"].location:
|
|
209
|
+
return ""
|
|
210
|
+
return render_component_template("circuits/inc/circuit_termination_cable_fragment.html", context)
|
|
211
|
+
return super().render_value(key, value, context)
|
|
212
|
+
|
|
213
|
+
def queryset_list_url_filter(self, key, value, context):
|
|
214
|
+
"""
|
|
215
|
+
Extend the panel rendering to render custom relationship information.
|
|
216
|
+
|
|
217
|
+
This is done for feature parity with the existing UI, and is not a pattern we *generally* should emulate.
|
|
218
|
+
"""
|
|
219
|
+
if isinstance(key, tuple):
|
|
220
|
+
# Copied from _ObjectRelationshipsPanel.queryset_list_url_filter()
|
|
221
|
+
relationship, side = key
|
|
222
|
+
termination = context["termination"]
|
|
223
|
+
return f"cr_{relationship.key}__{side}={termination.pk}"
|
|
224
|
+
return super().queryset_list_url_filter(key, value, context)
|
|
225
|
+
|
|
226
|
+
object_detail_content = ObjectDetailContent(
|
|
227
|
+
panels=(
|
|
228
|
+
ObjectFieldsPanel(
|
|
229
|
+
section=SectionChoices.LEFT_HALF,
|
|
230
|
+
weight=100,
|
|
231
|
+
fields="__all__",
|
|
232
|
+
exclude_fields=["comments", "circuit_termination_a", "circuit_termination_z"],
|
|
233
|
+
value_transforms={"commit_rate": [humanize_speed, placeholder]},
|
|
234
|
+
),
|
|
235
|
+
CircuitTerminationPanel(
|
|
236
|
+
label="Termination - A Side",
|
|
237
|
+
section=SectionChoices.RIGHT_HALF,
|
|
238
|
+
weight=100,
|
|
239
|
+
context_object_key="circuit_termination_a",
|
|
240
|
+
),
|
|
241
|
+
CircuitTerminationPanel(
|
|
242
|
+
label="Termination - Z Side",
|
|
243
|
+
section=SectionChoices.RIGHT_HALF,
|
|
244
|
+
weight=200,
|
|
245
|
+
context_object_key="circuit_termination_z",
|
|
246
|
+
),
|
|
247
|
+
),
|
|
248
|
+
)
|
|
132
249
|
|
|
133
250
|
def get_extra_context(self, request, instance):
|
|
134
251
|
context = super().get_extra_context(request, instance)
|
nautobot/cloud/api/views.py
CHANGED
|
@@ -9,40 +9,36 @@ from . import serializers
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class CloudAccountViewSet(NautobotModelViewSet):
|
|
12
|
-
queryset = models.CloudAccount.objects.
|
|
12
|
+
queryset = models.CloudAccount.objects.all()
|
|
13
13
|
serializer_class = serializers.CloudAccountSerializer
|
|
14
14
|
filterset_class = filters.CloudAccountFilterSet
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class CloudResourceTypeViewSet(NautobotModelViewSet):
|
|
18
|
-
queryset = models.CloudResourceType.objects.
|
|
18
|
+
queryset = models.CloudResourceType.objects.all()
|
|
19
19
|
serializer_class = serializers.CloudResourceTypeSerializer
|
|
20
20
|
filterset_class = filters.CloudResourceTypeFilterSet
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class CloudNetworkViewSet(NautobotModelViewSet):
|
|
24
|
-
queryset = models.CloudNetwork.objects.
|
|
25
|
-
"cloud_resource_type", "cloud_account", "parent"
|
|
26
|
-
).prefetch_related("prefixes")
|
|
24
|
+
queryset = models.CloudNetwork.objects.all()
|
|
27
25
|
serializer_class = serializers.CloudNetworkSerializer
|
|
28
26
|
filterset_class = filters.CloudNetworkFilterSet
|
|
29
27
|
|
|
30
28
|
|
|
31
29
|
class CloudNetworkPrefixAssignmentViewSet(NautobotModelViewSet):
|
|
32
|
-
queryset = models.CloudNetworkPrefixAssignment.objects.
|
|
30
|
+
queryset = models.CloudNetworkPrefixAssignment.objects.all()
|
|
33
31
|
serializer_class = serializers.CloudNetworkPrefixAssignmentSerializer
|
|
34
32
|
filterset_class = filters.CloudNetworkPrefixAssignmentFilterSet
|
|
35
33
|
|
|
36
34
|
|
|
37
35
|
class CloudServiceViewSet(NautobotModelViewSet):
|
|
38
|
-
queryset = models.CloudService.objects.
|
|
39
|
-
"cloud_networks"
|
|
40
|
-
)
|
|
36
|
+
queryset = models.CloudService.objects.all()
|
|
41
37
|
serializer_class = serializers.CloudServiceSerializer
|
|
42
38
|
filterset_class = filters.CloudServiceFilterSet
|
|
43
39
|
|
|
44
40
|
|
|
45
41
|
class CloudServiceNetworkAssignmentViewSet(NautobotModelViewSet):
|
|
46
|
-
queryset = models.CloudServiceNetworkAssignment.objects.
|
|
42
|
+
queryset = models.CloudServiceNetworkAssignment.objects.all()
|
|
47
43
|
serializer_class = serializers.CloudServiceNetworkAssignmentSerializer
|
|
48
44
|
filterset_class = filters.CloudServiceNetworkAssignmentFilterSet
|
nautobot/cloud/factory.py
CHANGED
|
@@ -86,7 +86,10 @@ class CloudNetworkFactory(PrimaryModelFactory):
|
|
|
86
86
|
if extracted:
|
|
87
87
|
self.prefixes.set(extracted)
|
|
88
88
|
else:
|
|
89
|
-
|
|
89
|
+
# TODO Investigate https://github.com/nautobot/nautobot/actions/runs/11019738391/job/30603271529
|
|
90
|
+
# to uncomment the line below.
|
|
91
|
+
# self.prefixes.set(get_random_instances(Prefix))
|
|
92
|
+
self.prefixes.set(get_random_instances(model_or_queryset_or_lambda=Prefix, maximum=1))
|
|
90
93
|
|
|
91
94
|
|
|
92
95
|
class CloudServiceFactory(PrimaryModelFactory):
|
|
@@ -5,12 +5,13 @@ from nautobot.core.testing import FilterTestCases
|
|
|
5
5
|
from nautobot.extras.models import SecretsGroup
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class CloudAccountTestCase(FilterTestCases.
|
|
8
|
+
class CloudAccountTestCase(FilterTestCases.FilterTestCase):
|
|
9
9
|
queryset = models.CloudAccount.objects.all()
|
|
10
10
|
filterset = filters.CloudAccountFilterSet
|
|
11
11
|
generic_filter_tests = [
|
|
12
12
|
("account_number",),
|
|
13
13
|
("description",),
|
|
14
|
+
("name",),
|
|
14
15
|
("provider", "provider__id"),
|
|
15
16
|
("provider", "provider__name"),
|
|
16
17
|
("secrets_group", "secrets_group__id"),
|
|
@@ -31,7 +32,7 @@ class CloudAccountTestCase(FilterTestCases.NameOnlyFilterTestCase):
|
|
|
31
32
|
cls.cloud_accounts[i].validated_save()
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
class CloudResourceTypeTestCase(FilterTestCases.
|
|
35
|
+
class CloudResourceTypeTestCase(FilterTestCases.FilterTestCase):
|
|
35
36
|
queryset = models.CloudResourceType.objects.all()
|
|
36
37
|
filterset = filters.CloudResourceTypeFilterSet
|
|
37
38
|
generic_filter_tests = [
|
|
@@ -50,7 +51,7 @@ class CloudResourceTypeTestCase(FilterTestCases.NameOnlyFilterTestCase):
|
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
class CloudNetworkTestCase(FilterTestCases.
|
|
54
|
+
class CloudNetworkTestCase(FilterTestCases.FilterTestCase):
|
|
54
55
|
queryset = models.CloudNetwork.objects.all()
|
|
55
56
|
filterset = filters.CloudNetworkFilterSet
|
|
56
57
|
generic_filter_tests = [
|
|
@@ -110,7 +111,7 @@ class CloudServiceNetworkAssignmentTestCase(FilterTestCases.FilterTestCase):
|
|
|
110
111
|
]
|
|
111
112
|
|
|
112
113
|
|
|
113
|
-
class CloudServiceTestCase(FilterTestCases.
|
|
114
|
+
class CloudServiceTestCase(FilterTestCases.FilterTestCase):
|
|
114
115
|
queryset = models.CloudService.objects.all()
|
|
115
116
|
filterset = filters.CloudServiceFilterSet
|
|
116
117
|
generic_filter_tests = [
|
nautobot/cloud/views.py
CHANGED
|
@@ -100,16 +100,6 @@ class CloudNetworkUIViewSet(NautobotUIViewSet):
|
|
|
100
100
|
|
|
101
101
|
return context
|
|
102
102
|
|
|
103
|
-
def extra_post_save_action(self, obj, form):
|
|
104
|
-
if form.cleaned_data.get("add_prefixes", None):
|
|
105
|
-
obj.prefixes.add(*form.cleaned_data["add_prefixes"])
|
|
106
|
-
if form.cleaned_data.get("remove_prefixes", None):
|
|
107
|
-
obj.prefixes.remove(*form.cleaned_data["remove_prefixes"])
|
|
108
|
-
if form.cleaned_data.get("add_cloud_services", None):
|
|
109
|
-
obj.cloud_services.add(*form.cleaned_data["add_cloud_services"])
|
|
110
|
-
if form.cleaned_data.get("remove_cloud_services", None):
|
|
111
|
-
obj.cloud_services.remove(*form.cleaned_data["remove_cloud_services"])
|
|
112
|
-
|
|
113
103
|
|
|
114
104
|
class CloudResourceTypeUIViewSet(NautobotUIViewSet):
|
|
115
105
|
queryset = CloudResourceType.objects.all()
|
|
@@ -173,9 +163,3 @@ class CloudServiceUIViewSet(NautobotUIViewSet):
|
|
|
173
163
|
)
|
|
174
164
|
|
|
175
165
|
return context
|
|
176
|
-
|
|
177
|
-
def extra_post_save_action(self, obj, form):
|
|
178
|
-
if form.cleaned_data.get("add_cloud_networks", None):
|
|
179
|
-
obj.cloud_networks.add(*form.cleaned_data["add_cloud_networks"])
|
|
180
|
-
if form.cleaned_data.get("remove_cloud_networks", None):
|
|
181
|
-
obj.cloud_networks.remove(*form.cleaned_data["remove_cloud_networks"])
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Query parameters that are *not* filterset filters
|
|
2
|
+
NON_FILTER_QUERY_PARAMS = (
|
|
3
|
+
"api_version", # used to select the Nautobot API version
|
|
4
|
+
"depth", # nested levels of the serializers default to depth=0
|
|
5
|
+
"exclude_m2m", # used to exclude many-to-many fields from the REST API
|
|
6
|
+
"format", # "json" or "api", used in the interactive HTML REST API views
|
|
7
|
+
"include", # used to include computed fields, relationships, config-contexts, etc. (excluded by default)
|
|
8
|
+
"limit", # pagination
|
|
9
|
+
"offset", # pagination
|
|
10
|
+
"sort", # sorting of results
|
|
11
|
+
)
|
|
@@ -2,6 +2,8 @@ from django_filters.rest_framework.backends import DjangoFilterBackend
|
|
|
2
2
|
from rest_framework.filters import OrderingFilter
|
|
3
3
|
from tree_queries.models import TreeNode
|
|
4
4
|
|
|
5
|
+
from nautobot.core.api.constants import NON_FILTER_QUERY_PARAMS
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class NautobotFilterBackend(DjangoFilterBackend):
|
|
7
9
|
"""Custom filtering backend for use with django-rest-framework and django-filters."""
|
|
@@ -17,15 +19,7 @@ class NautobotFilterBackend(DjangoFilterBackend):
|
|
|
17
19
|
# The default 'data' is a reference to request.GET, which is immutable; copies of this data are mutable
|
|
18
20
|
data = kwargs["data"].copy()
|
|
19
21
|
|
|
20
|
-
for non_filter_param in
|
|
21
|
-
"api_version", # used to select the Nautobot API version
|
|
22
|
-
"depth", # nested levels of the serializers default to depth=0
|
|
23
|
-
"format", # "json" or "api", used in the interactive HTML REST API views
|
|
24
|
-
"include", # used to include computed fields, relationships, config-contexts, etc. (excluded by default)
|
|
25
|
-
"limit", # pagination
|
|
26
|
-
"offset", # pagination
|
|
27
|
-
"sort", # sorting of results
|
|
28
|
-
):
|
|
22
|
+
for non_filter_param in NON_FILTER_QUERY_PARAMS:
|
|
29
23
|
data.pop(non_filter_param, None)
|
|
30
24
|
|
|
31
25
|
kwargs["data"] = data
|
nautobot/core/api/metadata.py
CHANGED
|
@@ -1,202 +1,25 @@
|
|
|
1
|
-
import contextlib
|
|
2
|
-
from typing import Any, Dict, List
|
|
3
|
-
|
|
4
1
|
from django.core.exceptions import PermissionDenied
|
|
5
2
|
from django.http import Http404
|
|
6
|
-
from django.
|
|
7
|
-
from
|
|
8
|
-
import django_filters
|
|
9
|
-
import drf_react_template.schema_form_encoder as schema
|
|
10
|
-
from rest_framework import exceptions, fields as drf_fields, serializers as drf_serializers
|
|
3
|
+
from django.utils.encoding import force_str
|
|
4
|
+
from rest_framework import exceptions
|
|
11
5
|
from rest_framework.metadata import SimpleMetadata
|
|
12
6
|
from rest_framework.request import clone_request
|
|
13
7
|
|
|
14
|
-
from nautobot.core.
|
|
15
|
-
from nautobot.core.utils.lookup import get_route_for_model
|
|
16
|
-
|
|
17
|
-
# FIXME(jathan): I hate this pattern that these fields are hard-coded here. But for the moment, this
|
|
18
|
-
# works reliably.
|
|
19
|
-
BOTTOM_FIELDS = ["computed_fields", "custom_fields", "relationships"]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class NautobotProcessingMixin(schema.ProcessingMixin):
|
|
23
|
-
"""Processing mixin to account for custom field types and behaviors for Nautobot."""
|
|
24
|
-
|
|
25
|
-
def _get_type_map_value(self, field: schema.SerializerType):
|
|
26
|
-
"""Overload default to add "required" as a default mapping."""
|
|
27
|
-
# This adds "required" as a default type mapping compared to drf_react_template core.
|
|
28
|
-
result = {
|
|
29
|
-
"type": field.style.get("schema:type"),
|
|
30
|
-
"enum": field.style.get("schema:enum"),
|
|
31
|
-
"format": field.style.get("schema:format"),
|
|
32
|
-
"widget": field.style.get("ui:widget"),
|
|
33
|
-
"required": field.style.get("schema:required"),
|
|
34
|
-
"readOnly": field.style.get("schema:readOnly"),
|
|
35
|
-
}
|
|
36
|
-
result_default = self.TYPE_MAP.get(type(field).__name__, {})
|
|
37
|
-
for k in result_default:
|
|
38
|
-
if result[k] is None:
|
|
39
|
-
result[k] = result_default[k]
|
|
40
|
-
return result
|
|
41
|
-
|
|
42
|
-
def order_fields(self, fields):
|
|
43
|
-
"""Explicitly order the "big ugly" fields to the bottom."""
|
|
44
|
-
# FIXME(jathan): Correct the behavior introduced in #3500 by switching to `__all__` to
|
|
45
|
-
# assert these get added at the end.
|
|
46
|
-
for field_name in BOTTOM_FIELDS:
|
|
47
|
-
if field_name in fields:
|
|
48
|
-
fields.remove(field_name)
|
|
49
|
-
fields.append(field_name)
|
|
50
|
-
return fields
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class NautobotSchemaProcessor(NautobotProcessingMixin, schema.SchemaProcessor):
|
|
54
|
-
"""SchemaProcessor to account for custom field types and behaviors for Nautobot."""
|
|
55
|
-
|
|
56
|
-
def _get_field_properties(self, field: schema.SerializerType, name: str) -> Dict[str, Any]:
|
|
57
|
-
"""
|
|
58
|
-
This method is used to generate the proper schema based on serializer field mappings or
|
|
59
|
-
per-field attribute markup.
|
|
60
|
-
|
|
61
|
-
This has been overloaded with an `elif` to account for `ManyRelatedField`.
|
|
62
|
-
"""
|
|
63
|
-
type_map_obj = self._get_type_map_value(field)
|
|
64
|
-
result = {
|
|
65
|
-
"type": type_map_obj["type"] or "string",
|
|
66
|
-
"title": self._get_title(field, name),
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if isinstance(field, drf_serializers.ListField):
|
|
70
|
-
if field.allow_empty:
|
|
71
|
-
result["required"] = not getattr(field, "allow_empty", True)
|
|
72
|
-
result["items"] = self._get_field_properties(field.child, "")
|
|
73
|
-
result["uniqueItems"] = True
|
|
74
|
-
elif isinstance(field, drf_serializers.ManyRelatedField):
|
|
75
|
-
if field.allow_empty:
|
|
76
|
-
result["required"] = type_map_obj.get("required", [])
|
|
77
|
-
result["items"] = self._get_field_properties(field.child_relation, "")
|
|
78
|
-
result["uniqueItems"] = True
|
|
79
|
-
else:
|
|
80
|
-
if isinstance(field, drf_serializers.RelatedField):
|
|
81
|
-
result["uniqueItems"] = True
|
|
82
|
-
if hasattr(field, "queryset") and hasattr(field.queryset, "model"):
|
|
83
|
-
# We construct the correct list view url here so we can just append the object id to the end of the url in our frontend.
|
|
84
|
-
# e.g. /dcim/locations/
|
|
85
|
-
try:
|
|
86
|
-
model_url = reverse(get_route_for_model(model=field.queryset.model, action="list"))
|
|
87
|
-
except NoReverseMatch:
|
|
88
|
-
model_url = None
|
|
89
|
-
model_options = field.queryset.model._meta
|
|
90
|
-
result["required"] = field.required
|
|
91
|
-
# Custom Keyword: modelName, modelNamePlural and appLabel
|
|
92
|
-
# modelName represents the model name of the uuid model
|
|
93
|
-
# modelUrl represents the UI URL to list instances of this model
|
|
94
|
-
# and appLabel represents the app_name of the model
|
|
95
|
-
result["modelName"] = model_options.model_name
|
|
96
|
-
result["appLabel"] = model_options.app_label
|
|
97
|
-
result["modelUrl"] = model_url
|
|
98
|
-
if field.allow_null:
|
|
99
|
-
result["type"] = [result["type"], "null"]
|
|
100
|
-
if enum := type_map_obj.get("enum"):
|
|
101
|
-
if enum == "choices":
|
|
102
|
-
choices = field.choices
|
|
103
|
-
result["enum"] = list(choices.keys())
|
|
104
|
-
result["enumNames"] = list(choices.values())
|
|
105
|
-
if isinstance(enum, (list, tuple)):
|
|
106
|
-
result["enum"] = [item[0] for item in enum]
|
|
107
|
-
result["enumNames"] = [item[1] for item in enum]
|
|
108
|
-
if format_ := type_map_obj["format"]:
|
|
109
|
-
result["format"] = format_
|
|
110
|
-
|
|
111
|
-
if read_only := field.read_only:
|
|
112
|
-
result["readOnly"] = read_only
|
|
113
|
-
|
|
114
|
-
with contextlib.suppress(drf_fields.SkipField):
|
|
115
|
-
result["default"] = field.get_default()
|
|
116
|
-
|
|
117
|
-
result = self._set_validation_properties(field, result)
|
|
118
|
-
|
|
119
|
-
return result
|
|
120
|
-
|
|
121
|
-
@staticmethod
|
|
122
|
-
def _filter_fields(all_fields):
|
|
123
|
-
"""
|
|
124
|
-
Override super._filter_fields to return all fields, including read-only fields,
|
|
125
|
-
as read-only fields have to be displayed in Detail View.
|
|
126
|
-
"""
|
|
127
|
-
return tuple((name, field) for name, field in all_fields)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class NautobotUiSchemaProcessor(NautobotProcessingMixin, schema.UiSchemaProcessor):
|
|
131
|
-
"""UiSchemaProcessor to account for custom field types and behaviors for Nautobot."""
|
|
132
|
-
|
|
133
|
-
def _field_order(self) -> List[str]:
|
|
134
|
-
"""
|
|
135
|
-
Overload the base which just returns `Meta.fields` and doesn't play nicely with "__all__".
|
|
136
|
-
|
|
137
|
-
This instead calls `get_fields()` and returns the keys.
|
|
138
|
-
"""
|
|
139
|
-
if self._is_list_serializer(self.serializer):
|
|
140
|
-
fields = self.serializer.child.get_fields()
|
|
141
|
-
else:
|
|
142
|
-
fields = self.serializer.get_fields()
|
|
143
|
-
|
|
144
|
-
field_names = self.order_fields(list(fields))
|
|
145
|
-
|
|
146
|
-
return field_names
|
|
147
|
-
|
|
148
|
-
def _get_ui_field_properties(self, field: schema.SerializerType, name: str) -> Dict[str, Any]:
|
|
149
|
-
"""
|
|
150
|
-
We had to overload this here to make it so that array types with children validate properly
|
|
151
|
-
and to also use `NautobotUiSchemaProcessor` over the default.
|
|
152
|
-
"""
|
|
153
|
-
data_index = self._generate_data_index(name)
|
|
154
|
-
result = {}
|
|
155
|
-
is_int = False
|
|
156
|
-
is_list = False
|
|
157
|
-
if self._is_field_serializer(field):
|
|
158
|
-
return NautobotUiSchemaProcessor(field, self.renderer_context, prefix=data_index).get_ui_schema()
|
|
159
|
-
elif isinstance(field, drf_serializers.ListField):
|
|
160
|
-
is_list = True
|
|
161
|
-
child = field.child
|
|
162
|
-
is_int = isinstance(child, drf_serializers.IntegerField)
|
|
163
|
-
widget = self._get_type_map_value(field=child).get("widget")
|
|
164
|
-
if not widget and isinstance(child, drf_serializers.ChoiceField):
|
|
165
|
-
widget = "checkbox"
|
|
166
|
-
else:
|
|
167
|
-
widget = self._get_type_map_value(field=field).get("widget")
|
|
168
|
-
help_text = field.help_text
|
|
169
|
-
if widget:
|
|
170
|
-
if is_list and is_int:
|
|
171
|
-
if "items" not in result:
|
|
172
|
-
result["items"] = {}
|
|
173
|
-
result["items"]["ui:widget"] = widget
|
|
174
|
-
else:
|
|
175
|
-
result["ui:widget"] = widget
|
|
176
|
-
if help_text:
|
|
177
|
-
result["ui:help"] = help_text
|
|
178
|
-
result.update(self._get_style_dict(field))
|
|
179
|
-
result = self._set_validation_properties(field, result)
|
|
180
|
-
return result
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
class NautobotColumnProcessor(NautobotProcessingMixin, schema.ColumnProcessor):
|
|
184
|
-
"""ColumnProcessor to account for custom field types and behaviors for Nautobot."""
|
|
8
|
+
from nautobot.core.api import ContentTypeField
|
|
185
9
|
|
|
186
10
|
|
|
187
11
|
class NautobotMetadata(SimpleMetadata):
|
|
188
12
|
"""
|
|
189
|
-
Metadata class that emits JSON schema.
|
|
190
|
-
|
|
191
|
-
- schema: The object JSON schema
|
|
192
|
-
- uiSchema: The object UI schema which describes the form layout in the UI
|
|
13
|
+
Metadata class that emits JSON schema.
|
|
193
14
|
"""
|
|
194
15
|
|
|
195
16
|
def determine_actions(self, request, view):
|
|
196
17
|
"""Generate the actions and return the names of the allowed methods."""
|
|
197
|
-
actions =
|
|
18
|
+
actions = {}
|
|
198
19
|
for method in {"PUT", "POST"} & set(view.allowed_methods):
|
|
199
20
|
view.request = clone_request(request, method)
|
|
21
|
+
view.request.major_version = request.major_version
|
|
22
|
+
view.request.minor_version = request.minor_version
|
|
200
23
|
try:
|
|
201
24
|
# Test global permissions
|
|
202
25
|
if hasattr(view, "check_permissions"):
|
|
@@ -207,82 +30,31 @@ class NautobotMetadata(SimpleMetadata):
|
|
|
207
30
|
except (exceptions.APIException, PermissionDenied, Http404):
|
|
208
31
|
pass
|
|
209
32
|
else:
|
|
210
|
-
|
|
33
|
+
# If user has appropriate permissions for the view, include
|
|
34
|
+
# appropriate metadata about the fields that should be supplied.
|
|
35
|
+
serializer = view.get_serializer()
|
|
36
|
+
actions[method] = self.get_serializer_info(serializer)
|
|
211
37
|
finally:
|
|
212
38
|
view.request = request
|
|
213
39
|
return actions
|
|
214
40
|
|
|
215
|
-
def
|
|
216
|
-
"""
|
|
217
|
-
|
|
218
|
-
return list(getattr(serializer_meta, "list_display_fields", []))
|
|
219
|
-
|
|
220
|
-
def determine_metadata(self, request, view):
|
|
221
|
-
"""This is the metadata that gets returned on an `OPTIONS` request."""
|
|
222
|
-
metadata = super().determine_metadata(request, view)
|
|
223
|
-
|
|
224
|
-
# Include the object type label for this model.
|
|
225
|
-
object_type = view.queryset.model._meta.label_lower if getattr(view, "queryset", None) else "unknown"
|
|
226
|
-
metadata["object_type"] = object_type
|
|
41
|
+
def get_field_info(self, field):
|
|
42
|
+
"""
|
|
43
|
+
Fixup field information:
|
|
227
44
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
ui_schema = NautobotUiSchemaProcessor(serializer, request.parser_context).get_ui_schema()
|
|
237
|
-
ui_schema["ui:order"] = [*self.get_list_display_fields(serializer), "*"]
|
|
238
|
-
metadata.update(
|
|
45
|
+
- Set choices for ContentTypeField
|
|
46
|
+
- Replace DRF choices `display_name` to `display` to match new pattern.
|
|
47
|
+
"""
|
|
48
|
+
field_info = super().get_field_info(field)
|
|
49
|
+
for choice in field_info.get("choices", []):
|
|
50
|
+
choice["display"] = choice.pop("display_name")
|
|
51
|
+
if hasattr(field, "queryset") and not field_info.get("read_only") and isinstance(field, ContentTypeField):
|
|
52
|
+
field_info["choices"] = [
|
|
239
53
|
{
|
|
240
|
-
"
|
|
241
|
-
"
|
|
54
|
+
"value": choice_value,
|
|
55
|
+
"display": force_str(choice_name, strings_only=True),
|
|
242
56
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if hasattr(serializer, "determine_view_options"):
|
|
248
|
-
metadata["view_options"] = serializer.determine_view_options(request)
|
|
249
|
-
|
|
250
|
-
return metadata
|
|
251
|
-
|
|
252
|
-
def get_filter_info(self, view):
|
|
253
|
-
"""Enumerate filterset information for the view. Returns a dictionary with the following format:
|
|
254
|
-
|
|
255
|
-
{
|
|
256
|
-
"filter_name": {
|
|
257
|
-
"label": "Filter Label",
|
|
258
|
-
"lookup_types": [
|
|
259
|
-
{"value": "filter_name__n", "label": "not exact (n)"},
|
|
260
|
-
{"value": "filter_name__re", "label": "matches regex (re)"},
|
|
261
|
-
...
|
|
262
|
-
]
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
if not getattr(view, "filterset_class", None):
|
|
269
|
-
return {}
|
|
270
|
-
filterset = view.filterset_class
|
|
271
|
-
filters = {}
|
|
272
|
-
for filter_name, filter_instance in sorted(
|
|
273
|
-
filterset.base_filters.items(),
|
|
274
|
-
key=lambda x: get_filter_field_label(x[1]),
|
|
275
|
-
):
|
|
276
|
-
filter_key = filter_name.rsplit("__", 1)[0]
|
|
277
|
-
label = get_filter_field_label(filter_instance)
|
|
278
|
-
lookup_label = self._filter_lookup_label(filter_name, filter_instance)
|
|
279
|
-
filters.setdefault(filter_key, {"label": label})
|
|
280
|
-
filters[filter_key].setdefault("lookup_types", []).append({"value": filter_name, "label": lookup_label})
|
|
281
|
-
return filters
|
|
282
|
-
|
|
283
|
-
# TODO: move this into `build_lookup_label` when the legacy UI is removed
|
|
284
|
-
def _filter_lookup_label(self, filter_name, filter_instance):
|
|
285
|
-
"""Fix confusing lookup labels for boolean filters."""
|
|
286
|
-
if isinstance(filter_instance, django_filters.BooleanFilter):
|
|
287
|
-
return "exact"
|
|
288
|
-
return build_lookup_label(filter_name, filter_instance.lookup_expr)
|
|
57
|
+
for choice_value, choice_name in field.choices.items()
|
|
58
|
+
]
|
|
59
|
+
field_info["choices"].sort(key=lambda item: item["display"])
|
|
60
|
+
return field_info
|