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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from django.template import Context
|
|
2
|
+
from django.template.loader import get_template
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def flatten_context(context) -> dict:
|
|
6
|
+
"""
|
|
7
|
+
If the given context is already a dict, return it unmodified; if it is a Context, flatten it to a dict.
|
|
8
|
+
|
|
9
|
+
This is working around a bug (not sure if in Django or in our code) where, if a `Context`'s `dicts` contain
|
|
10
|
+
a `RequestContext`, calling `Context.flatten()` throws an exception.
|
|
11
|
+
"""
|
|
12
|
+
if isinstance(context, dict):
|
|
13
|
+
return context
|
|
14
|
+
flat = {}
|
|
15
|
+
for d in context.dicts:
|
|
16
|
+
if isinstance(d, dict):
|
|
17
|
+
flat.update(d)
|
|
18
|
+
else:
|
|
19
|
+
flat.update(flatten_context(d))
|
|
20
|
+
return flat
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def render_component_template(template_path: str, context: Context, **kwargs) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Render the template located at the given path with the given context, possibly augmented via additional kwargs.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
template_path (str): Path to the template to render, for example `"components/tab/label_wrapper.html"`.
|
|
29
|
+
context (Context): Rendering context for the template
|
|
30
|
+
**kwargs (dict): Additional key/value pairs to extend the context with for this specific template.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> render_component_template(self.label_wrapper_template_path, context, tab_id=self.tab_id, label="Hello")
|
|
34
|
+
"""
|
|
35
|
+
with context.update(kwargs):
|
|
36
|
+
return get_template(template_path).render(flatten_context(context))
|
nautobot/core/urls.py
CHANGED
|
@@ -5,11 +5,13 @@ from django.views.generic import TemplateView
|
|
|
5
5
|
from django.views.static import serve
|
|
6
6
|
|
|
7
7
|
from nautobot.core.views import (
|
|
8
|
+
AboutView,
|
|
8
9
|
CustomGraphQLView,
|
|
9
10
|
get_file_with_authorization,
|
|
10
11
|
HomeView,
|
|
11
12
|
NautobotMetricsView,
|
|
12
13
|
NautobotMetricsViewAuth,
|
|
14
|
+
RenderJinjaView,
|
|
13
15
|
SearchView,
|
|
14
16
|
StaticMediaFailureView,
|
|
15
17
|
ThemePreviewView,
|
|
@@ -27,6 +29,7 @@ from .admin import admin_site
|
|
|
27
29
|
urlpatterns = [
|
|
28
30
|
# Base views
|
|
29
31
|
path("", HomeView.as_view(), name="home"),
|
|
32
|
+
path("about/", AboutView.as_view(), name="about"),
|
|
30
33
|
path("search/", SearchView.as_view(), name="search"),
|
|
31
34
|
# Login/logout
|
|
32
35
|
path("login/", LoginView.as_view(), name="login"),
|
|
@@ -42,6 +45,7 @@ urlpatterns = [
|
|
|
42
45
|
path("user/", include("nautobot.users.urls")),
|
|
43
46
|
path("users/", include("nautobot.users.urls", "users")),
|
|
44
47
|
path("virtualization/", include("nautobot.virtualization.urls")),
|
|
48
|
+
path("wireless/", include("nautobot.wireless.urls")),
|
|
45
49
|
# API
|
|
46
50
|
path("api/", include("nautobot.core.api.urls")),
|
|
47
51
|
# GraphQL
|
|
@@ -69,6 +73,8 @@ urlpatterns = [
|
|
|
69
73
|
),
|
|
70
74
|
# Celery worker status page
|
|
71
75
|
path("worker-status/", WorkerStatusView.as_view(), name="worker_status"),
|
|
76
|
+
# Jinja template renderer tool
|
|
77
|
+
path("render-jinja-template/", RenderJinjaView.as_view(), name="render_jinja_template"),
|
|
72
78
|
# Templated css file
|
|
73
79
|
path(
|
|
74
80
|
"template.css", TemplateView.as_view(template_name="template.css", content_type="text/css"), name="template_css"
|
nautobot/core/utils/config.py
CHANGED
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
"""Helper code for loading values that may be defined in settings.py/nautobot_config.py *or* in django-constance."""
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
4
|
+
import logging
|
|
5
|
+
|
|
3
6
|
from constance import config
|
|
4
7
|
from django.conf import settings
|
|
8
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
9
|
+
from django.db import OperationalError, ProgrammingError
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
5
13
|
|
|
14
|
+
def get_settings_or_config(variable_name, fallback=None):
|
|
15
|
+
"""
|
|
16
|
+
Get a value from Django settings (if specified there) or Constance configuration (otherwise).
|
|
6
17
|
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
The fallback value is returned *only* if the requested variable cannot be found at all - this is an error case,
|
|
19
|
+
and will generate warning logs.
|
|
20
|
+
"""
|
|
9
21
|
# Explicitly set in settings.py or nautobot_config.py takes precedence, for now
|
|
10
22
|
if hasattr(settings, variable_name):
|
|
11
23
|
return getattr(settings, variable_name)
|
|
12
|
-
|
|
24
|
+
# django-constance 4.x removed some built-in error handling here, so we have to do it ourselves now
|
|
25
|
+
with contextlib.suppress(ObjectDoesNotExist, OperationalError, ProgrammingError):
|
|
26
|
+
return getattr(config, variable_name)
|
|
27
|
+
logger.warning(
|
|
28
|
+
'Configuration "%s" is not in settings, and could not read from the Constance database table '
|
|
29
|
+
"(perhaps not initialized yet?)",
|
|
30
|
+
variable_name,
|
|
31
|
+
)
|
|
32
|
+
if variable_name in settings.CONSTANCE_CONFIG:
|
|
33
|
+
default = settings.CONSTANCE_CONFIG[variable_name][0]
|
|
34
|
+
logger.warning('Using default value of "%s" from Constance configuration for "%s"', default, variable_name)
|
|
35
|
+
return default
|
|
36
|
+
logger.warning(
|
|
37
|
+
'Constance configuration does not include an entry for "%s" - must return %s', variable_name, fallback
|
|
38
|
+
)
|
|
39
|
+
return fallback
|
nautobot/core/utils/lookup.py
CHANGED
|
@@ -10,6 +10,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
10
10
|
from django.db.models import Model
|
|
11
11
|
from django.urls import get_resolver, resolve, reverse, URLPattern, URLResolver
|
|
12
12
|
from django.utils.module_loading import import_string
|
|
13
|
+
from django.views.generic.base import RedirectView
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def get_changes_for_model(model):
|
|
@@ -315,7 +316,7 @@ def get_created_and_last_updated_usernames_for_model(instance):
|
|
|
315
316
|
return created_by, last_updated_by
|
|
316
317
|
|
|
317
318
|
|
|
318
|
-
def get_url_patterns(urlconf=None, patterns_list=None, base_path="/"):
|
|
319
|
+
def get_url_patterns(urlconf=None, patterns_list=None, base_path="/", ignore_redirects=False):
|
|
319
320
|
"""
|
|
320
321
|
Recursively yield a list of registered URL patterns.
|
|
321
322
|
|
|
@@ -326,6 +327,7 @@ def get_url_patterns(urlconf=None, patterns_list=None, base_path="/"):
|
|
|
326
327
|
Default if unspecified is the `url_patterns` attribute of the given `urlconf` module.
|
|
327
328
|
base_path (str): String to prepend to all URL patterns yielded.
|
|
328
329
|
Default if unspecified is the string `"/"`.
|
|
330
|
+
ignore_redirects (bool): If True, skip URL patterns that correspond to RedirectViews.
|
|
329
331
|
|
|
330
332
|
Yields:
|
|
331
333
|
(str): Each URL pattern defined in the given urlconf and its descendants
|
|
@@ -384,10 +386,18 @@ def get_url_patterns(urlconf=None, patterns_list=None, base_path="/"):
|
|
|
384
386
|
|
|
385
387
|
for item in patterns_list:
|
|
386
388
|
if isinstance(item, URLPattern):
|
|
389
|
+
if (
|
|
390
|
+
ignore_redirects
|
|
391
|
+
and hasattr(item.callback, "view_class")
|
|
392
|
+
and issubclass(item.callback.view_class, RedirectView)
|
|
393
|
+
):
|
|
394
|
+
continue
|
|
387
395
|
yield base_path + str(item.pattern)
|
|
388
396
|
elif isinstance(item, URLResolver):
|
|
389
397
|
# Recurse!
|
|
390
|
-
yield from get_url_patterns(
|
|
398
|
+
yield from get_url_patterns(
|
|
399
|
+
urlconf, item.url_patterns, base_path + str(item.pattern), ignore_redirects=ignore_redirects
|
|
400
|
+
)
|
|
391
401
|
|
|
392
402
|
|
|
393
403
|
def get_url_for_url_pattern(url_pattern):
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.db import NotSupportedError
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def maybe_select_related(queryset, select_fields):
|
|
9
|
+
"""Attempt to perform a select_related() on the given queryset, but fail gracefully if not permitted."""
|
|
10
|
+
model = queryset.model
|
|
11
|
+
|
|
12
|
+
# Django doesn't allow .select_related() on a QuerySet that had .values()/.values_list() applied, or
|
|
13
|
+
# one that has had union()/intersection()/difference() applied.
|
|
14
|
+
# We can detect and avoid these cases the same way that Django itself does.
|
|
15
|
+
if queryset._fields is not None:
|
|
16
|
+
logger.debug(
|
|
17
|
+
"NOT applying select_related(%s) to %s QuerySet as it includes .values()/.values_list()",
|
|
18
|
+
select_fields,
|
|
19
|
+
model.__name__,
|
|
20
|
+
)
|
|
21
|
+
elif queryset.query.combinator:
|
|
22
|
+
logger.debug(
|
|
23
|
+
"NOT applying select_related(%s) to %s QuerySet as it is a combinator query",
|
|
24
|
+
select_fields,
|
|
25
|
+
model.__name__,
|
|
26
|
+
)
|
|
27
|
+
else:
|
|
28
|
+
logger.debug("Applying .select_related(%s) to %s QuerySet", select_fields, model.__name__)
|
|
29
|
+
# Belt and suspenders - we should have avoided any error cases above, but be safe anyway:
|
|
30
|
+
try:
|
|
31
|
+
queryset = queryset.select_related(*select_fields)
|
|
32
|
+
except (TypeError, ValueError, NotSupportedError) as exc:
|
|
33
|
+
logger.warning(
|
|
34
|
+
"Unexpected error when trying to .select_related() on %s QuerySet: %s",
|
|
35
|
+
model.__name__,
|
|
36
|
+
exc,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return queryset
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def maybe_prefetch_related(queryset, prefetch_fields):
|
|
43
|
+
"""Attempt to perform a select_related() on the given queryset, but fail gracefully if not permitted."""
|
|
44
|
+
model = queryset.model
|
|
45
|
+
|
|
46
|
+
if queryset.query.combinator:
|
|
47
|
+
logger.debug(
|
|
48
|
+
"NOT applying prefetch_related(%s) to %s QuerySet as it is a combinator query",
|
|
49
|
+
prefetch_fields,
|
|
50
|
+
model.__name__,
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
logger.debug("Applying .prefetch_related(%s) to %s QuerySet", prefetch_fields, model.__name__)
|
|
54
|
+
# Belt and suspenders - we should have avoided any error cases above, but be safe anyway:
|
|
55
|
+
try:
|
|
56
|
+
queryset = queryset.prefetch_related(*prefetch_fields)
|
|
57
|
+
except (AttributeError, TypeError, ValueError, NotSupportedError) as exc:
|
|
58
|
+
logger.warning(
|
|
59
|
+
"Unexpected error when trying to .prefetch_related() on %s QuerySet: %s",
|
|
60
|
+
model.__name__,
|
|
61
|
+
exc,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return queryset
|
nautobot/core/utils/requests.py
CHANGED
|
@@ -141,22 +141,35 @@ def get_filterable_params_from_filter_params(filter_params, non_filter_params, f
|
|
|
141
141
|
return final_filter_params
|
|
142
142
|
|
|
143
143
|
|
|
144
|
-
def normalize_querydict(querydict, form_class=None):
|
|
144
|
+
def normalize_querydict(querydict, form_class=None, filterset=None):
|
|
145
145
|
"""
|
|
146
|
-
|
|
146
|
+
Converts a QueryDict into a standard, mutable dictionary while preserving multiple values as lists.
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
Example:
|
|
149
|
+
A QueryDict like:
|
|
150
|
+
QueryDict('foo=1&bar=2&bar=3&baz=')
|
|
149
151
|
|
|
150
|
-
|
|
152
|
+
Converts to:
|
|
153
|
+
{'foo': '1', 'bar': ['2', '3'], 'baz': ''}
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
This function ensures that fields with multiple values are handled correctly, as QueryDict
|
|
156
|
+
does not inherently preserve multiple values as lists.
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
Args:
|
|
159
|
+
querydict (QueryDict): The QueryDict to be normalized.
|
|
160
|
+
form_class (forms.Form, optional): A form class to identify fields that should be treated as
|
|
161
|
+
lists (e.g., `MultipleChoiceField` or `ModelMultipleChoiceField`).
|
|
162
|
+
filterset (django_filters.FilterSet, optional): A FilterSet instance to identify filters that
|
|
163
|
+
should preserve multiple values as lists (e.g., non-single-choice fields).
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
AttributeError: If both `form_class` and `filterset` are provided.
|
|
158
167
|
"""
|
|
168
|
+
if form_class and filterset:
|
|
169
|
+
raise AttributeError("Either form_class or filterset_class is to be provided not both")
|
|
170
|
+
|
|
159
171
|
result = {}
|
|
172
|
+
|
|
160
173
|
if querydict:
|
|
161
174
|
for key, value_list in querydict.lists():
|
|
162
175
|
if len(value_list) > 1:
|
|
@@ -171,6 +184,8 @@ def normalize_querydict(querydict, form_class=None):
|
|
|
171
184
|
):
|
|
172
185
|
# Even though there's only a single value in the querydict for this key, the form wants it as a list
|
|
173
186
|
result[key] = value_list
|
|
187
|
+
elif filterset is not None and filterset.filters.get(key) and not is_single_choice_field(filterset, key):
|
|
188
|
+
result[key] = value_list
|
|
174
189
|
else:
|
|
175
190
|
# Only a single value in the querydict for this key, and no guidance otherwise, so make it single
|
|
176
191
|
result[key] = value_list[0]
|
nautobot/core/views/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
+
import datetime
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
4
5
|
import platform
|
|
@@ -45,6 +46,7 @@ from nautobot.core.celery import app
|
|
|
45
46
|
from nautobot.core.constants import SEARCH_MAX_RESULTS
|
|
46
47
|
from nautobot.core.forms import SearchForm
|
|
47
48
|
from nautobot.core.releases import get_latest_release
|
|
49
|
+
from nautobot.core.utils.config import get_settings_or_config
|
|
48
50
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
49
51
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
50
52
|
from nautobot.extras.forms import GraphQLQueryForm
|
|
@@ -56,7 +58,6 @@ logger = logging.getLogger(__name__)
|
|
|
56
58
|
|
|
57
59
|
class HomeView(AccessMixin, TemplateView):
|
|
58
60
|
template_name = "home.html"
|
|
59
|
-
use_new_ui = True
|
|
60
61
|
|
|
61
62
|
def render_additional_content(self, request, context, details):
|
|
62
63
|
# Collect all custom data using callback functions.
|
|
@@ -479,3 +480,49 @@ def get_file_with_authorization(request, *args, **kwargs):
|
|
|
479
480
|
get_object_or_404(queryset, file=request.GET.get("name"))
|
|
480
481
|
|
|
481
482
|
return get_file(request, *args, **kwargs)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
class AboutView(AccessMixin, TemplateView):
|
|
486
|
+
"""
|
|
487
|
+
Nautobot About View which displays general information about Nautobot and contact details
|
|
488
|
+
for Network to Code.
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
template_name = "about.html"
|
|
492
|
+
|
|
493
|
+
def get(self, request, *args, **kwargs):
|
|
494
|
+
# Redirect user to login page if not authenticated
|
|
495
|
+
if not request.user.is_authenticated:
|
|
496
|
+
return self.handle_no_permission()
|
|
497
|
+
# Check whether a new release is available. (Only for staff/superusers.)
|
|
498
|
+
new_release = None
|
|
499
|
+
if request.user.is_staff or request.user.is_superuser:
|
|
500
|
+
latest_release, release_url = get_latest_release()
|
|
501
|
+
if isinstance(latest_release, version.Version):
|
|
502
|
+
current_version = version.parse(settings.VERSION)
|
|
503
|
+
if latest_release > current_version:
|
|
504
|
+
new_release = {
|
|
505
|
+
"version": str(latest_release),
|
|
506
|
+
"url": release_url,
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
# Support contract state
|
|
510
|
+
support_expiration_date = get_settings_or_config("NTC_SUPPORT_CONTRACT_EXPIRATION_DATE")
|
|
511
|
+
support_contract_active = support_expiration_date and support_expiration_date >= datetime.date.today()
|
|
512
|
+
|
|
513
|
+
context = self.get_context_data()
|
|
514
|
+
context.update(
|
|
515
|
+
{
|
|
516
|
+
"new_release": new_release,
|
|
517
|
+
"support_contract_active": support_contract_active,
|
|
518
|
+
"support_expiration_date": support_expiration_date,
|
|
519
|
+
}
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
return self.render_to_response(context)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class RenderJinjaView(LoginRequiredMixin, TemplateView):
|
|
526
|
+
"""Render a Jinja template with context data."""
|
|
527
|
+
|
|
528
|
+
template_name = "utilities/render_jinja2.html"
|
nautobot/core/views/generic.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
from __future__ import annotations # python 3.8
|
|
2
|
-
|
|
3
1
|
from copy import deepcopy
|
|
4
2
|
import logging
|
|
5
3
|
import re
|
|
6
|
-
from typing import Optional
|
|
4
|
+
from typing import ClassVar, Optional
|
|
7
5
|
|
|
8
6
|
from django.conf import settings
|
|
9
7
|
from django.contrib import messages
|
|
@@ -11,12 +9,11 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
|
|
11
9
|
from django.contrib.auth.models import AnonymousUser
|
|
12
10
|
from django.contrib.contenttypes.models import ContentType
|
|
13
11
|
from django.core.exceptions import (
|
|
14
|
-
FieldDoesNotExist,
|
|
15
12
|
ObjectDoesNotExist,
|
|
16
13
|
ValidationError,
|
|
17
14
|
)
|
|
18
15
|
from django.db import IntegrityError, transaction
|
|
19
|
-
from django.db.models import
|
|
16
|
+
from django.db.models import Model, ProtectedError, Q, QuerySet
|
|
20
17
|
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
|
21
18
|
from django.http import HttpResponse
|
|
22
19
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
@@ -29,6 +26,7 @@ from django_filters import FilterSet
|
|
|
29
26
|
from django_tables2 import RequestConfig, Table
|
|
30
27
|
|
|
31
28
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
29
|
+
from nautobot.core.constants import MAX_PAGE_SIZE_DEFAULT
|
|
32
30
|
from nautobot.core.exceptions import AbortTransaction
|
|
33
31
|
from nautobot.core.forms import (
|
|
34
32
|
BootstrapMixin,
|
|
@@ -50,7 +48,7 @@ from nautobot.core.utils.requests import (
|
|
|
50
48
|
get_filterable_params_from_filter_params,
|
|
51
49
|
normalize_querydict,
|
|
52
50
|
)
|
|
53
|
-
from nautobot.core.views.mixins import
|
|
51
|
+
from nautobot.core.views.mixins import BulkEditAndBulkDeleteModelMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin
|
|
54
52
|
from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
|
|
55
53
|
from nautobot.core.views.utils import (
|
|
56
54
|
check_filter_for_display,
|
|
@@ -61,9 +59,7 @@ from nautobot.core.views.utils import (
|
|
|
61
59
|
prepare_cloned_fields,
|
|
62
60
|
view_changes_not_saved,
|
|
63
61
|
)
|
|
64
|
-
from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
|
|
65
62
|
from nautobot.extras.models import ExportTemplate, SavedView, UserSavedViewAssociation
|
|
66
|
-
from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
|
|
67
63
|
|
|
68
64
|
|
|
69
65
|
class GenericView(LoginRequiredMixin, View):
|
|
@@ -82,8 +78,9 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
|
|
|
82
78
|
template_name: Name of the template to use
|
|
83
79
|
"""
|
|
84
80
|
|
|
85
|
-
queryset: QuerySet
|
|
86
|
-
template_name: Optional[str] = None
|
|
81
|
+
queryset: ClassVar[QuerySet]
|
|
82
|
+
template_name: ClassVar[Optional[str]] = None
|
|
83
|
+
object_detail_content = None
|
|
87
84
|
|
|
88
85
|
def get_required_permission(self):
|
|
89
86
|
return get_permission_for_model(self.queryset.model, "view")
|
|
@@ -123,6 +120,7 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
|
|
|
123
120
|
"content_type": content_type,
|
|
124
121
|
"verbose_name": self.queryset.model._meta.verbose_name,
|
|
125
122
|
"verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
|
|
123
|
+
"object_detail_content": self.object_detail_content,
|
|
126
124
|
**common_detail_view_context(request, instance),
|
|
127
125
|
**self.get_extra_context(request, instance),
|
|
128
126
|
}
|
|
@@ -355,7 +353,7 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
|
|
|
355
353
|
}
|
|
356
354
|
RequestConfig(request, paginate).configure(table)
|
|
357
355
|
table_config_form = TableConfigForm(table=table)
|
|
358
|
-
max_page_size = get_settings_or_config("MAX_PAGE_SIZE")
|
|
356
|
+
max_page_size = get_settings_or_config("MAX_PAGE_SIZE", fallback=MAX_PAGE_SIZE_DEFAULT)
|
|
359
357
|
if max_page_size and paginate["per_page"] > max_page_size:
|
|
360
358
|
messages.warning(
|
|
361
359
|
request,
|
|
@@ -987,7 +985,7 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): #
|
|
|
987
985
|
)
|
|
988
986
|
|
|
989
987
|
|
|
990
|
-
class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin,
|
|
988
|
+
class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAndBulkDeleteModelMixin, View):
|
|
991
989
|
"""
|
|
992
990
|
Edit objects in bulk.
|
|
993
991
|
|
|
@@ -1037,93 +1035,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDele
|
|
|
1037
1035
|
|
|
1038
1036
|
if form.is_valid():
|
|
1039
1037
|
logger.debug("Form validation was successful")
|
|
1040
|
-
|
|
1041
|
-
form_relationships = getattr(form, "relationships", [])
|
|
1042
|
-
standard_fields = [
|
|
1043
|
-
field
|
|
1044
|
-
for field in form.fields
|
|
1045
|
-
if field not in form_custom_fields + form_relationships + ["pk"] + ["object_note"]
|
|
1046
|
-
]
|
|
1047
|
-
nullified_fields = request.POST.getlist("_nullify")
|
|
1048
|
-
|
|
1049
|
-
try:
|
|
1050
|
-
with deferred_change_logging_for_bulk_operation():
|
|
1051
|
-
updated_objects = []
|
|
1052
|
-
queryset = queryset if edit_all else queryset.filter(pk__in=form.cleaned_data["pk"])
|
|
1053
|
-
for obj in queryset:
|
|
1054
|
-
obj = self.alter_obj(obj, request, [], kwargs)
|
|
1055
|
-
|
|
1056
|
-
# Update standard fields. If a field is listed in _nullify, delete its value.
|
|
1057
|
-
for name in standard_fields:
|
|
1058
|
-
try:
|
|
1059
|
-
model_field = model._meta.get_field(name)
|
|
1060
|
-
except FieldDoesNotExist:
|
|
1061
|
-
# This form field is used to modify a field rather than set its value directly
|
|
1062
|
-
model_field = None
|
|
1063
|
-
|
|
1064
|
-
# Handle nullification
|
|
1065
|
-
if name in form.nullable_fields and name in nullified_fields:
|
|
1066
|
-
if isinstance(model_field, ManyToManyField):
|
|
1067
|
-
getattr(obj, name).set([])
|
|
1068
|
-
else:
|
|
1069
|
-
setattr(obj, name, None if model_field is not None and model_field.null else "")
|
|
1070
|
-
|
|
1071
|
-
# ManyToManyFields
|
|
1072
|
-
elif isinstance(model_field, ManyToManyField):
|
|
1073
|
-
if form.cleaned_data[name]:
|
|
1074
|
-
getattr(obj, name).set(form.cleaned_data[name])
|
|
1075
|
-
# Normal fields
|
|
1076
|
-
elif form.cleaned_data[name] not in (None, ""):
|
|
1077
|
-
setattr(obj, name, form.cleaned_data[name])
|
|
1078
|
-
|
|
1079
|
-
# Update custom fields
|
|
1080
|
-
for field_name in form_custom_fields:
|
|
1081
|
-
if field_name in form.nullable_fields and field_name in nullified_fields:
|
|
1082
|
-
obj.cf[remove_prefix_from_cf_key(field_name)] = None
|
|
1083
|
-
elif form.cleaned_data.get(field_name) not in (None, "", []):
|
|
1084
|
-
obj.cf[remove_prefix_from_cf_key(field_name)] = form.cleaned_data[field_name]
|
|
1085
|
-
|
|
1086
|
-
obj.full_clean()
|
|
1087
|
-
obj.save()
|
|
1088
|
-
updated_objects.append(obj)
|
|
1089
|
-
logger.debug(f"Saved {obj} (PK: {obj.pk})")
|
|
1090
|
-
|
|
1091
|
-
# Add/remove tags
|
|
1092
|
-
if form.cleaned_data.get("add_tags", None):
|
|
1093
|
-
obj.tags.add(*form.cleaned_data["add_tags"])
|
|
1094
|
-
if form.cleaned_data.get("remove_tags", None):
|
|
1095
|
-
obj.tags.remove(*form.cleaned_data["remove_tags"])
|
|
1096
|
-
|
|
1097
|
-
if hasattr(form, "save_relationships") and callable(form.save_relationships):
|
|
1098
|
-
# Add/remove relationship associations
|
|
1099
|
-
form.save_relationships(instance=obj, nullified_fields=nullified_fields)
|
|
1100
|
-
|
|
1101
|
-
if hasattr(form, "save_note") and callable(form.save_note):
|
|
1102
|
-
form.save_note(instance=obj, user=request.user)
|
|
1103
|
-
|
|
1104
|
-
self.extra_post_save_action(obj, form)
|
|
1105
|
-
|
|
1106
|
-
# Enforce object-level permissions
|
|
1107
|
-
if self.queryset.filter(pk__in=[obj.pk for obj in updated_objects]).count() != len(
|
|
1108
|
-
updated_objects
|
|
1109
|
-
):
|
|
1110
|
-
raise ObjectDoesNotExist
|
|
1111
|
-
|
|
1112
|
-
if updated_objects:
|
|
1113
|
-
msg = f"Updated {len(updated_objects)} {model._meta.verbose_name_plural}"
|
|
1114
|
-
logger.info(msg)
|
|
1115
|
-
messages.success(self.request, msg)
|
|
1116
|
-
|
|
1117
|
-
return redirect(self.get_return_url(request))
|
|
1118
|
-
|
|
1119
|
-
except ValidationError as e:
|
|
1120
|
-
messages.error(self.request, f"{obj} failed validation: {e}")
|
|
1121
|
-
|
|
1122
|
-
except ObjectDoesNotExist:
|
|
1123
|
-
msg = "Object update failed due to object-level permissions violation"
|
|
1124
|
-
logger.debug(msg)
|
|
1125
|
-
form.add_error(None, msg)
|
|
1126
|
-
|
|
1038
|
+
return self.send_bulk_edit_objects_to_job(request, form, model)
|
|
1127
1039
|
else:
|
|
1128
1040
|
logger.debug("Form validation failed")
|
|
1129
1041
|
|
|
@@ -1267,7 +1179,7 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
|
1267
1179
|
return ""
|
|
1268
1180
|
|
|
1269
1181
|
|
|
1270
|
-
class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin,
|
|
1182
|
+
class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAndBulkDeleteModelMixin, View):
|
|
1271
1183
|
"""
|
|
1272
1184
|
Delete objects in bulk.
|
|
1273
1185
|
|
|
@@ -1290,48 +1202,30 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDe
|
|
|
1290
1202
|
def get(self, request):
|
|
1291
1203
|
return redirect(self.get_return_url(request))
|
|
1292
1204
|
|
|
1293
|
-
def _perform_delete_operation(self, request, queryset, model):
|
|
1294
|
-
logger = logging.getLogger(__name__ + ".BulkDeleteView")
|
|
1295
|
-
self.perform_pre_delete(request, queryset)
|
|
1296
|
-
try:
|
|
1297
|
-
_, deleted_info = bulk_delete_with_bulk_change_logging(queryset)
|
|
1298
|
-
deleted_count = deleted_info[model._meta.label]
|
|
1299
|
-
except ProtectedError as e:
|
|
1300
|
-
logger.info("Caught ProtectedError while attempting to delete objects")
|
|
1301
|
-
handle_protectederror(queryset, request, e)
|
|
1302
|
-
return redirect(self.get_return_url(request))
|
|
1303
|
-
msg = f"Deleted {deleted_count} {model._meta.verbose_name_plural}"
|
|
1304
|
-
logger.info(msg)
|
|
1305
|
-
messages.success(request, msg)
|
|
1306
|
-
return redirect(self.get_return_url(request))
|
|
1307
|
-
|
|
1308
1205
|
def post(self, request, **kwargs):
|
|
1309
1206
|
logger = logging.getLogger(f"{__name__}.BulkDeleteView")
|
|
1310
1207
|
model = self.queryset.model
|
|
1208
|
+
delete_all = request.POST.get("_all")
|
|
1311
1209
|
|
|
1312
1210
|
# Are we deleting *all* objects in the queryset or just a selected subset?
|
|
1313
|
-
if
|
|
1211
|
+
if delete_all:
|
|
1314
1212
|
queryset = self._get_bulk_edit_delete_all_queryset(request)
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
context = self._bulk_delete_all_context(request, queryset)
|
|
1320
|
-
context.update(self.extra_context())
|
|
1321
|
-
return render(request, self.template_name, context)
|
|
1322
|
-
|
|
1323
|
-
pk_list = request.POST.getlist("pk")
|
|
1213
|
+
pk_list = []
|
|
1214
|
+
else:
|
|
1215
|
+
pk_list = request.POST.getlist("pk")
|
|
1216
|
+
queryset = self.queryset.filter(pk__in=pk_list)
|
|
1324
1217
|
|
|
1325
1218
|
form_cls = self.get_form()
|
|
1326
1219
|
|
|
1327
1220
|
if "_confirm" in request.POST:
|
|
1328
1221
|
form = form_cls(request.POST)
|
|
1222
|
+
# Set pk field requirement here instead of BulkDeleteForm since get_form() may return a different form class
|
|
1223
|
+
if delete_all:
|
|
1224
|
+
form.fields["pk"].required = False
|
|
1225
|
+
|
|
1329
1226
|
if form.is_valid():
|
|
1330
1227
|
logger.debug("Form validation was successful")
|
|
1331
|
-
|
|
1332
|
-
# Delete objects
|
|
1333
|
-
queryset = self.queryset.filter(pk__in=pk_list)
|
|
1334
|
-
return self._perform_delete_operation(request, queryset, model)
|
|
1228
|
+
return self.send_bulk_delete_objects_to_job(request, pk_list, model, delete_all)
|
|
1335
1229
|
else:
|
|
1336
1230
|
logger.debug("Form validation failed")
|
|
1337
1231
|
|
|
@@ -1344,23 +1238,26 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDe
|
|
|
1344
1238
|
)
|
|
1345
1239
|
|
|
1346
1240
|
# Retrieve objects being deleted
|
|
1347
|
-
table =
|
|
1348
|
-
if not
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1241
|
+
table = None
|
|
1242
|
+
if not delete_all:
|
|
1243
|
+
table = self.table(queryset, orderable=False)
|
|
1244
|
+
if not table.rows:
|
|
1245
|
+
messages.warning(
|
|
1246
|
+
request,
|
|
1247
|
+
f"No {model._meta.verbose_name_plural} were selected for deletion.",
|
|
1248
|
+
)
|
|
1249
|
+
return redirect(self.get_return_url(request))
|
|
1250
|
+
# Hide actions column if present
|
|
1251
|
+
if "actions" in table.columns:
|
|
1252
|
+
table.columns.hide("actions")
|
|
1357
1253
|
|
|
1358
1254
|
context = {
|
|
1359
1255
|
"form": form,
|
|
1360
1256
|
"obj_type_plural": model._meta.verbose_name_plural,
|
|
1361
1257
|
"table": table,
|
|
1362
1258
|
"return_url": self.get_return_url(request),
|
|
1363
|
-
"total_objs_to_delete":
|
|
1259
|
+
"total_objs_to_delete": queryset.count(),
|
|
1260
|
+
"delete_all": delete_all,
|
|
1364
1261
|
}
|
|
1365
1262
|
context.update(self.extra_context())
|
|
1366
1263
|
return render(request, self.template_name, context)
|