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/core/graphql/schema.py
CHANGED
|
@@ -43,7 +43,7 @@ from nautobot.dcim.graphql.types import (
|
|
|
43
43
|
RearPortType,
|
|
44
44
|
)
|
|
45
45
|
from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipSideChoices
|
|
46
|
-
from nautobot.extras.graphql.types import ContactAssociationType, DynamicGroupType, TagType
|
|
46
|
+
from nautobot.extras.graphql.types import ContactAssociationType, DynamicGroupType, JobType, ScheduledJobType, TagType
|
|
47
47
|
from nautobot.extras.models import ComputedField, CustomField, Relationship
|
|
48
48
|
from nautobot.extras.registry import registry
|
|
49
49
|
from nautobot.extras.utils import check_if_key_is_graphql_safe
|
|
@@ -71,6 +71,8 @@ registry["graphql_types"]["dcim.rearport"] = RearPortType
|
|
|
71
71
|
registry["graphql_types"]["dcim.location"] = LocationType
|
|
72
72
|
registry["graphql_types"]["extras.contactassociation"] = ContactAssociationType
|
|
73
73
|
registry["graphql_types"]["extras.dynamicgroup"] = DynamicGroupType
|
|
74
|
+
registry["graphql_types"]["extras.job"] = JobType
|
|
75
|
+
registry["graphql_types"]["extras.scheduledjob"] = ScheduledJobType
|
|
74
76
|
registry["graphql_types"]["extras.tag"] = TagType
|
|
75
77
|
registry["graphql_types"]["ipam.ipaddress"] = IPAddressType
|
|
76
78
|
registry["graphql_types"]["ipam.prefix"] = PrefixType
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -4,7 +4,9 @@ from io import BytesIO
|
|
|
4
4
|
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.contrib.contenttypes.models import ContentType
|
|
7
|
-
from django.core.exceptions import
|
|
7
|
+
from django.core.exceptions import (
|
|
8
|
+
PermissionDenied,
|
|
9
|
+
)
|
|
8
10
|
from django.db import transaction
|
|
9
11
|
from django.http import QueryDict
|
|
10
12
|
from rest_framework import exceptions as drf_exceptions
|
|
@@ -15,6 +17,7 @@ from nautobot.core.api.renderers import NautobotCSVRenderer
|
|
|
15
17
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
16
18
|
from nautobot.core.celery import app, register_jobs
|
|
17
19
|
from nautobot.core.exceptions import AbortTransaction
|
|
20
|
+
from nautobot.core.jobs.bulk_actions import BulkDeleteObjects, BulkEditObjects
|
|
18
21
|
from nautobot.core.jobs.cleanup import LogsCleanup
|
|
19
22
|
from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
|
|
20
23
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
@@ -25,7 +28,16 @@ from nautobot.extras.datasources import (
|
|
|
25
28
|
refresh_datasource_content,
|
|
26
29
|
refresh_job_code_from_repository,
|
|
27
30
|
)
|
|
28
|
-
from nautobot.extras.jobs import
|
|
31
|
+
from nautobot.extras.jobs import (
|
|
32
|
+
BooleanVar,
|
|
33
|
+
ChoiceVar,
|
|
34
|
+
FileVar,
|
|
35
|
+
Job,
|
|
36
|
+
ObjectVar,
|
|
37
|
+
RunJobTaskFailed,
|
|
38
|
+
StringVar,
|
|
39
|
+
TextVar,
|
|
40
|
+
)
|
|
29
41
|
from nautobot.extras.models import ExportTemplate, GitRepository
|
|
30
42
|
|
|
31
43
|
name = "System Jobs"
|
|
@@ -347,5 +359,14 @@ class ImportObjects(Job):
|
|
|
347
359
|
raise RunJobTaskFailed("CSV import not fully successful, see logs")
|
|
348
360
|
|
|
349
361
|
|
|
350
|
-
jobs = [
|
|
362
|
+
jobs = [
|
|
363
|
+
BulkDeleteObjects,
|
|
364
|
+
BulkEditObjects,
|
|
365
|
+
ExportObjectList,
|
|
366
|
+
GitRepositorySync,
|
|
367
|
+
GitRepositoryDryRun,
|
|
368
|
+
ImportObjects,
|
|
369
|
+
LogsCleanup,
|
|
370
|
+
RefreshDynamicGroupCaches,
|
|
371
|
+
]
|
|
351
372
|
register_jobs(*jobs)
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
2
|
+
from django.core.exceptions import (
|
|
3
|
+
FieldDoesNotExist,
|
|
4
|
+
ObjectDoesNotExist,
|
|
5
|
+
PermissionDenied,
|
|
6
|
+
ValidationError,
|
|
7
|
+
)
|
|
8
|
+
from django.db.models import ManyToManyField, ProtectedError
|
|
9
|
+
|
|
10
|
+
from nautobot.core.forms.utils import restrict_form_fields
|
|
11
|
+
from nautobot.core.utils.lookup import get_filterset_for_model, get_form_for_model
|
|
12
|
+
from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
|
|
13
|
+
from nautobot.extras.jobs import (
|
|
14
|
+
BooleanVar,
|
|
15
|
+
Job,
|
|
16
|
+
JSONVar,
|
|
17
|
+
ObjectVar,
|
|
18
|
+
RunJobTaskFailed,
|
|
19
|
+
)
|
|
20
|
+
from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
|
|
21
|
+
|
|
22
|
+
name = "System Jobs"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BulkEditObjects(Job):
|
|
26
|
+
"""System Job to bulk Edit objects."""
|
|
27
|
+
|
|
28
|
+
content_type = ObjectVar(
|
|
29
|
+
model=ContentType,
|
|
30
|
+
description="Type of objects to update",
|
|
31
|
+
)
|
|
32
|
+
form_data = JSONVar(description="BulkEditForm data")
|
|
33
|
+
edit_all = BooleanVar(description="Bulk Edit all object / all filtered objects", required=False)
|
|
34
|
+
filter_query_params = JSONVar(label="Filter Query Params", required=False)
|
|
35
|
+
|
|
36
|
+
class Meta:
|
|
37
|
+
name = "Bulk Edit Objects"
|
|
38
|
+
description = "Bulk edit objects."
|
|
39
|
+
has_sensitive_variables = False
|
|
40
|
+
soft_time_limit = 1800
|
|
41
|
+
time_limit = 2000
|
|
42
|
+
|
|
43
|
+
def _update_objects(self, model, form, filter_query_params, edit_all, nullified_fields):
|
|
44
|
+
with deferred_change_logging_for_bulk_operation():
|
|
45
|
+
updated_objects_pk = []
|
|
46
|
+
filterset_cls = get_filterset_for_model(model)
|
|
47
|
+
|
|
48
|
+
if filter_query_params and not filterset_cls:
|
|
49
|
+
self.logger.error(f"Filterset not found for {model}")
|
|
50
|
+
raise RunJobTaskFailed(f"Filter query provided but {model} do not have a filterset.")
|
|
51
|
+
|
|
52
|
+
if edit_all:
|
|
53
|
+
if filterset_cls and filter_query_params:
|
|
54
|
+
queryset = filterset_cls(filter_query_params).qs.restrict(self.user, "change")
|
|
55
|
+
else:
|
|
56
|
+
queryset = model.objects.restrict(self.user, "change")
|
|
57
|
+
else:
|
|
58
|
+
queryset = model.objects.restrict(self.user, "change").filter(pk__in=form.cleaned_data["pk"])
|
|
59
|
+
|
|
60
|
+
form_custom_fields = getattr(form, "custom_fields", [])
|
|
61
|
+
form_relationships = getattr(form, "relationships", [])
|
|
62
|
+
standard_fields = [
|
|
63
|
+
field
|
|
64
|
+
for field in form.fields
|
|
65
|
+
if field not in form_custom_fields + form_relationships + ["pk"] + ["object_note"]
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
self.logger.debug(f"Performing update on {queryset.count()} {model._meta.verbose_name_plural}")
|
|
69
|
+
for obj in queryset.iterator():
|
|
70
|
+
# Update standard fields. If a field is listed in _nullify, delete its value.
|
|
71
|
+
for field_name in standard_fields:
|
|
72
|
+
try:
|
|
73
|
+
model_field = model._meta.get_field(field_name)
|
|
74
|
+
except FieldDoesNotExist:
|
|
75
|
+
# This form field is used to modify a field rather than set its value directly
|
|
76
|
+
model_field = None
|
|
77
|
+
|
|
78
|
+
# Handle nullification
|
|
79
|
+
if nullified_fields:
|
|
80
|
+
if field_name in form.nullable_fields and field_name in nullified_fields:
|
|
81
|
+
if isinstance(model_field, ManyToManyField):
|
|
82
|
+
getattr(obj, field_name).set([])
|
|
83
|
+
else:
|
|
84
|
+
setattr(obj, field_name, None if model_field is not None and model_field.null else "")
|
|
85
|
+
|
|
86
|
+
# ManyToManyFields
|
|
87
|
+
elif isinstance(model_field, ManyToManyField):
|
|
88
|
+
if form.cleaned_data[field_name]:
|
|
89
|
+
getattr(obj, field_name).set(form.cleaned_data[field_name])
|
|
90
|
+
# Normal fields
|
|
91
|
+
elif form.cleaned_data[field_name] not in (None, ""):
|
|
92
|
+
setattr(obj, field_name, form.cleaned_data[field_name])
|
|
93
|
+
|
|
94
|
+
# Update custom fields
|
|
95
|
+
for field_name in form_custom_fields:
|
|
96
|
+
if field_name in form.nullable_fields and field_name in nullified_fields:
|
|
97
|
+
obj.cf[remove_prefix_from_cf_key(field_name)] = None
|
|
98
|
+
elif form.cleaned_data.get(field_name) not in (None, "", []):
|
|
99
|
+
obj.cf[remove_prefix_from_cf_key(field_name)] = form.cleaned_data[field_name]
|
|
100
|
+
|
|
101
|
+
obj.full_clean()
|
|
102
|
+
obj.save()
|
|
103
|
+
updated_objects_pk.append(obj.pk)
|
|
104
|
+
form.post_save(obj)
|
|
105
|
+
|
|
106
|
+
if hasattr(form, "save_relationships") and callable(form.save_relationships):
|
|
107
|
+
# Add/remove relationship associations
|
|
108
|
+
form.save_relationships(instance=obj, nullified_fields=nullified_fields)
|
|
109
|
+
|
|
110
|
+
if hasattr(form, "save_note") and callable(form.save_note):
|
|
111
|
+
form.save_note(instance=obj, user=self.user)
|
|
112
|
+
total_updated_objs = len(updated_objects_pk)
|
|
113
|
+
# Enforce object-level permissions
|
|
114
|
+
if queryset.filter(pk__in=updated_objects_pk).count() != total_updated_objs:
|
|
115
|
+
raise ObjectDoesNotExist
|
|
116
|
+
return total_updated_objs
|
|
117
|
+
|
|
118
|
+
def _process_valid_form(self, model, form, filter_query_params, edit_all, nullified_fields):
|
|
119
|
+
try:
|
|
120
|
+
total_updated_objs = self._update_objects(model, form, filter_query_params, edit_all, nullified_fields)
|
|
121
|
+
msg = f"Updated {total_updated_objs} {model._meta.verbose_name_plural}"
|
|
122
|
+
self.logger.info(msg)
|
|
123
|
+
return msg
|
|
124
|
+
except ValidationError as e:
|
|
125
|
+
self.logger.error(str(e))
|
|
126
|
+
except ObjectDoesNotExist:
|
|
127
|
+
msg = "Object update failed due to object-level permissions violation"
|
|
128
|
+
self.logger.error(msg)
|
|
129
|
+
raise RunJobTaskFailed("Bulk Edit not fully successful, see logs")
|
|
130
|
+
|
|
131
|
+
def run( # pylint: disable=arguments-differ
|
|
132
|
+
self, *, content_type, form_data, edit_all=False, filter_query_params=None
|
|
133
|
+
):
|
|
134
|
+
if not self.user.has_perm(f"{content_type.app_label}.change_{content_type.model}"):
|
|
135
|
+
self.logger.error('User "%s" does not have permission to update %s objects', self.user, content_type.model)
|
|
136
|
+
raise PermissionDenied("User does not have change permissions on the requested content-type")
|
|
137
|
+
|
|
138
|
+
model = content_type.model_class()
|
|
139
|
+
if model is None:
|
|
140
|
+
self.logger.error(
|
|
141
|
+
'Could not find the "%s.%s" data model. Perhaps an app is uninstalled?',
|
|
142
|
+
content_type.app_label,
|
|
143
|
+
content_type.model,
|
|
144
|
+
)
|
|
145
|
+
raise RunJobTaskFailed("Model not found")
|
|
146
|
+
try:
|
|
147
|
+
form_cls = get_form_for_model(model, form_prefix="BulkEdit")
|
|
148
|
+
except Exception:
|
|
149
|
+
self.logger.debug(
|
|
150
|
+
'Could not find the "%s.%s" data bulk edit form. Unable to process Bulk Edit for this model.',
|
|
151
|
+
content_type.app_label,
|
|
152
|
+
content_type.model,
|
|
153
|
+
)
|
|
154
|
+
raise
|
|
155
|
+
form = form_cls(model, form_data, edit_all=edit_all)
|
|
156
|
+
restrict_form_fields(form, self.user)
|
|
157
|
+
|
|
158
|
+
if form.is_valid():
|
|
159
|
+
self.logger.debug("Form validation was successful")
|
|
160
|
+
nullified_fields = form_data.get("_nullify")
|
|
161
|
+
return self._process_valid_form(model, form, filter_query_params, edit_all, nullified_fields)
|
|
162
|
+
else:
|
|
163
|
+
self.logger.error(f"Form validation unsuccessful: {form.errors.as_json()}")
|
|
164
|
+
|
|
165
|
+
raise RunJobTaskFailed("Updating Jobs Failed")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class BulkDeleteObjects(Job):
|
|
169
|
+
"""
|
|
170
|
+
System job to bulk delete objects.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
content_type = ObjectVar(
|
|
174
|
+
model=ContentType,
|
|
175
|
+
description="Type of objects to delete",
|
|
176
|
+
)
|
|
177
|
+
pk_list = JSONVar(description="List of objects pks to delete", required=False)
|
|
178
|
+
delete_all = BooleanVar(description="Delete all (filtered) objects instead of a list of PKs", required=False)
|
|
179
|
+
filter_query_params = JSONVar(label="Filter Query Params", required=False)
|
|
180
|
+
|
|
181
|
+
class Meta:
|
|
182
|
+
name = "Bulk Delete Objects"
|
|
183
|
+
description = "Bulk delete objects."
|
|
184
|
+
has_sensitive_variables = False
|
|
185
|
+
soft_time_limit = 1800
|
|
186
|
+
time_limit = 2000
|
|
187
|
+
|
|
188
|
+
def run( # pylint: disable=arguments-differ
|
|
189
|
+
self, *, content_type, pk_list=None, delete_all=False, filter_query_params=None
|
|
190
|
+
):
|
|
191
|
+
if not self.user.has_perm(f"{content_type.app_label}.delete_{content_type.model}"):
|
|
192
|
+
self.logger.error('User "%s" does not have permission to delete %s objects', self.user, content_type.model)
|
|
193
|
+
raise PermissionDenied("User does not have delete permissions on the requested content-type")
|
|
194
|
+
|
|
195
|
+
model = content_type.model_class()
|
|
196
|
+
if model is None:
|
|
197
|
+
self.logger.error(
|
|
198
|
+
'Could not find the "%s.%s" data model. Perhaps an app is uninstalled?',
|
|
199
|
+
content_type.app_label,
|
|
200
|
+
content_type.model,
|
|
201
|
+
)
|
|
202
|
+
raise RunJobTaskFailed("Model not found")
|
|
203
|
+
|
|
204
|
+
if pk_list and (delete_all or filter_query_params):
|
|
205
|
+
self.logger.error(
|
|
206
|
+
"You can either delete objs within `pk_list` provided or `delete_all` with `filter_query_params` if needed."
|
|
207
|
+
)
|
|
208
|
+
raise RunJobTaskFailed("Both `pk_list` and `delete_all` can not both be set.")
|
|
209
|
+
|
|
210
|
+
filterset_cls = get_filterset_for_model(model)
|
|
211
|
+
|
|
212
|
+
if filter_query_params and not filterset_cls:
|
|
213
|
+
self.logger.error(f"Filterset not found for {model}")
|
|
214
|
+
raise RunJobTaskFailed(f"Filter query provided but {model} do not have a filterset.")
|
|
215
|
+
|
|
216
|
+
if delete_all:
|
|
217
|
+
if filterset_cls and filter_query_params:
|
|
218
|
+
queryset = filterset_cls(filter_query_params).qs.restrict(self.user, "delete")
|
|
219
|
+
# We take this approach because filterset.qs has already applied .distinct(),
|
|
220
|
+
# and performing a .delete directly on a queryset with .distinct applied is not allowed.
|
|
221
|
+
queryset = model.objects.filter(pk__in=queryset)
|
|
222
|
+
else:
|
|
223
|
+
queryset = model.objects.restrict(self.user, "delete")
|
|
224
|
+
else:
|
|
225
|
+
queryset = model.objects.restrict(self.user, "delete").filter(pk__in=pk_list)
|
|
226
|
+
if queryset.count() < len(pk_list):
|
|
227
|
+
# We do not check ObjectPermission error for `delete_all` case because user is trying to
|
|
228
|
+
# delete all objs they have permission to which would not raise any issue.
|
|
229
|
+
self.logger.error("You do not have permissions to delete some of the objects provided in `pk_list`.")
|
|
230
|
+
raise RunJobTaskFailed("Caught ObjectPermission error while attempting to delete objects")
|
|
231
|
+
|
|
232
|
+
verbose_name_plural = model._meta.verbose_name_plural
|
|
233
|
+
|
|
234
|
+
# Currently the only purpose of a bulk delete form is to perform a `pre_delete` operation
|
|
235
|
+
if form_cls := get_form_for_model(model, form_prefix="BulkDelete"):
|
|
236
|
+
form = form_cls(model, {"pk": pk_list}, delete_all=delete_all)
|
|
237
|
+
if hasattr(form, "perform_pre_delete"):
|
|
238
|
+
form.perform_pre_delete(queryset)
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
self.logger.info(f"Deleting {queryset.count()} {verbose_name_plural}...")
|
|
242
|
+
_, deleted_info = bulk_delete_with_bulk_change_logging(queryset)
|
|
243
|
+
deleted_count = deleted_info[model._meta.label]
|
|
244
|
+
except ProtectedError as err:
|
|
245
|
+
self.logger.error(f"Caught ProtectedError while attempting to delete objects: {err}")
|
|
246
|
+
raise RunJobTaskFailed("Caught ProtectedError while attempting to delete objects")
|
|
247
|
+
msg = f"Deleted {deleted_count} {model._meta.verbose_name_plural}"
|
|
248
|
+
self.logger.info(msg)
|
nautobot/core/jobs/cleanup.py
CHANGED
|
@@ -72,7 +72,7 @@ class LogsCleanup(Job):
|
|
|
72
72
|
|
|
73
73
|
def run(self, *, cleanup_types, max_age=None): # pylint: disable=arguments-differ
|
|
74
74
|
if max_age in (None, ""):
|
|
75
|
-
max_age = get_settings_or_config("CHANGELOG_RETENTION")
|
|
75
|
+
max_age = get_settings_or_config("CHANGELOG_RETENTION", fallback=90)
|
|
76
76
|
if max_age == 0:
|
|
77
77
|
self.logger.warning(
|
|
78
78
|
"CHANGELOG_RETENTION setting is set to zero, disabling this Job. "
|
|
@@ -98,6 +98,7 @@ class Command(BaseCommand):
|
|
|
98
98
|
RearPortTemplateFactory,
|
|
99
99
|
SoftwareImageFileFactory,
|
|
100
100
|
SoftwareVersionFactory,
|
|
101
|
+
VirtualDeviceContextFactory,
|
|
101
102
|
)
|
|
102
103
|
from nautobot.extras.choices import MetadataTypeDataTypeChoices
|
|
103
104
|
from nautobot.extras.factory import (
|
|
@@ -105,6 +106,7 @@ class Command(BaseCommand):
|
|
|
105
106
|
DynamicGroupFactory,
|
|
106
107
|
ExternalIntegrationFactory,
|
|
107
108
|
JobLogEntryFactory,
|
|
109
|
+
JobQueueFactory,
|
|
108
110
|
JobResultFactory,
|
|
109
111
|
MetadataChoiceFactory,
|
|
110
112
|
MetadataTypeFactory,
|
|
@@ -131,6 +133,14 @@ class Command(BaseCommand):
|
|
|
131
133
|
)
|
|
132
134
|
from nautobot.tenancy.factory import TenantFactory, TenantGroupFactory
|
|
133
135
|
from nautobot.users.factory import UserFactory
|
|
136
|
+
from nautobot.wireless.factory import (
|
|
137
|
+
ControllerManagedDeviceGroupWithMembersFactory,
|
|
138
|
+
RadioProfileFactory,
|
|
139
|
+
RadioProfilesWithMembersFactory,
|
|
140
|
+
SupportedDataRateFactory,
|
|
141
|
+
WirelessNetworkFactory,
|
|
142
|
+
WirelessNetworksWithMembersFactory,
|
|
143
|
+
)
|
|
134
144
|
except ImportError as err:
|
|
135
145
|
raise CommandError('Unable to load data factories. Is the "factory-boy" package installed?') from err
|
|
136
146
|
|
|
@@ -218,6 +228,7 @@ class Command(BaseCommand):
|
|
|
218
228
|
_create_batch(ManufacturerFactory, 2, description="without Platforms or DeviceTypes") # Last 2 hard-coded
|
|
219
229
|
_create_batch(DeviceRedundancyGroupFactory, 20)
|
|
220
230
|
_create_batch(DeviceFactory, 20)
|
|
231
|
+
_create_batch(VirtualDeviceContextFactory, 30)
|
|
221
232
|
_create_batch(ModuleFactory, 20)
|
|
222
233
|
_create_batch(SoftwareVersionFactory, 5, description="without Devices")
|
|
223
234
|
_create_batch(SoftwareImageFileFactory, 5, description="without DeviceTypes")
|
|
@@ -304,6 +315,15 @@ class Command(BaseCommand):
|
|
|
304
315
|
_create_batch(ExternalIntegrationFactory, 20)
|
|
305
316
|
_create_batch(ControllerFactory, 10, description="with Devices or DeviceRedundancyGroups")
|
|
306
317
|
_create_batch(ControllerManagedDeviceGroupFactory, 5, description="without any Devices")
|
|
318
|
+
_create_batch(SupportedDataRateFactory, 20)
|
|
319
|
+
_create_batch(RadioProfileFactory, 20)
|
|
320
|
+
_create_batch(WirelessNetworkFactory, 20)
|
|
321
|
+
_create_batch(ControllerManagedDeviceGroupWithMembersFactory, 5, description="with members")
|
|
322
|
+
_create_batch(RadioProfilesWithMembersFactory, 5, description="with members")
|
|
323
|
+
_create_batch(WirelessNetworksWithMembersFactory, 5, description="with members")
|
|
324
|
+
# make sure we have some supported data rates that have null relationships to make filter tests happy
|
|
325
|
+
_create_batch(SupportedDataRateFactory, 10, description="without any associated objects")
|
|
326
|
+
_create_batch(JobQueueFactory, 10)
|
|
307
327
|
# make sure we have some tenants that have null relationships to make filter tests happy
|
|
308
328
|
_create_batch(TenantFactory, 10, description="without any associated objects")
|
|
309
329
|
# TODO: nautobot.tenancy.tests.test_filters currently calls the following additional factories:
|
|
@@ -329,6 +349,7 @@ class Command(BaseCommand):
|
|
|
329
349
|
)
|
|
330
350
|
_create_batch(MetadataChoiceFactory, 100)
|
|
331
351
|
_create_batch(ObjectChangeFactory, 100)
|
|
352
|
+
_create_batch(JobQueueFactory, 30)
|
|
332
353
|
_create_batch(JobResultFactory, 20)
|
|
333
354
|
_create_batch(JobLogEntryFactory, 100)
|
|
334
355
|
_create_batch(ObjectMetadataFactory, 100)
|
nautobot/core/middleware.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from zoneinfo import ZoneInfo
|
|
2
|
+
|
|
1
3
|
from django.conf import settings
|
|
2
4
|
from django.contrib.auth.middleware import RemoteUserMiddleware as RemoteUserMiddleware_
|
|
3
5
|
from django.db import ProgrammingError
|
|
4
6
|
from django.http import Http404
|
|
5
7
|
from django.urls import resolve
|
|
6
8
|
from django.urls.exceptions import Resolver404
|
|
9
|
+
from django.utils import timezone
|
|
7
10
|
from django.utils.deprecation import MiddlewareMixin
|
|
8
11
|
|
|
9
12
|
from nautobot.core.api.utils import is_api_request, rest_api_server_error
|
|
@@ -149,3 +152,16 @@ class ExceptionHandlingMiddleware:
|
|
|
149
152
|
return server_error(request, template_name=custom_template)
|
|
150
153
|
|
|
151
154
|
return None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class UserDefinedTimeZoneMiddleware:
|
|
158
|
+
def __init__(self, get_response):
|
|
159
|
+
self.get_response = get_response
|
|
160
|
+
|
|
161
|
+
def __call__(self, request):
|
|
162
|
+
if request.user.is_authenticated:
|
|
163
|
+
if tzname := request.user.get_config("timezone"):
|
|
164
|
+
timezone.activate(ZoneInfo(tzname))
|
|
165
|
+
else:
|
|
166
|
+
timezone.deactivate()
|
|
167
|
+
return self.get_response(request)
|
nautobot/core/models/fields.py
CHANGED
|
@@ -280,6 +280,8 @@ class JSONArrayField(models.JSONField):
|
|
|
280
280
|
"""
|
|
281
281
|
An ArrayField implementation backed JSON storage.
|
|
282
282
|
Replicates ArrayField's base field validation.
|
|
283
|
+
|
|
284
|
+
Supports choices in the base field.
|
|
283
285
|
"""
|
|
284
286
|
|
|
285
287
|
_default_hint = ("list", "[]")
|
|
@@ -375,13 +377,15 @@ class JSONArrayField(models.JSONField):
|
|
|
375
377
|
|
|
376
378
|
def formfield(self, **kwargs):
|
|
377
379
|
"""Return a django.forms.Field instance for this field."""
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
380
|
+
defaults = {
|
|
381
|
+
"form_class": fields.JSONArrayFormField,
|
|
382
|
+
"base_field": self.base_field.formfield(),
|
|
383
|
+
}
|
|
384
|
+
# If the base field has choices, pass them to the form field.
|
|
385
|
+
if self.base_field.choices:
|
|
386
|
+
defaults["choices"] = self.base_field.choices
|
|
387
|
+
defaults.update(**kwargs)
|
|
388
|
+
return super().formfield(**defaults)
|
|
385
389
|
|
|
386
390
|
|
|
387
391
|
class LaxURLField(models.URLField):
|
nautobot/core/settings.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import datetime
|
|
1
2
|
import os
|
|
2
3
|
import os.path
|
|
3
4
|
import platform
|
|
@@ -10,7 +11,11 @@ import django.forms
|
|
|
10
11
|
from django.utils.safestring import mark_safe
|
|
11
12
|
|
|
12
13
|
from nautobot import __version__
|
|
13
|
-
from nautobot.core.constants import
|
|
14
|
+
from nautobot.core.constants import (
|
|
15
|
+
CONFIG_SETTING_SEPARATOR as _CONFIG_SETTING_SEPARATOR,
|
|
16
|
+
MAX_PAGE_SIZE_DEFAULT as _MAX_PAGE_SIZE_DEFAULT,
|
|
17
|
+
PAGINATE_COUNT_DEFAULT as _PAGINATE_COUNT_DEFAULT,
|
|
18
|
+
)
|
|
14
19
|
from nautobot.core.settings_funcs import ConstanceConfigItem, is_truthy, parse_redis_connection
|
|
15
20
|
|
|
16
21
|
#
|
|
@@ -96,6 +101,9 @@ if "NAUTOBOT_DEPLOYMENT_ID" in os.environ and os.environ["NAUTOBOT_DEPLOYMENT_ID
|
|
|
96
101
|
if "NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY" in os.environ and os.environ["NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"] != "":
|
|
97
102
|
DEVICE_NAME_AS_NATURAL_KEY = is_truthy(os.environ["NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"])
|
|
98
103
|
|
|
104
|
+
# Event Brokers
|
|
105
|
+
EVENT_BROKERS = {}
|
|
106
|
+
|
|
99
107
|
# Exclude potentially sensitive models from wildcard view exemption. These may still be exempted
|
|
100
108
|
# by specifying the model individually in the EXEMPT_VIEW_PERMISSIONS configuration parameter.
|
|
101
109
|
EXEMPT_EXCLUDE_MODELS = (
|
|
@@ -156,6 +164,16 @@ NAPALM_PASSWORD = os.getenv("NAUTOBOT_NAPALM_PASSWORD", "")
|
|
|
156
164
|
NAPALM_TIMEOUT = int(os.getenv("NAUTOBOT_NAPALM_TIMEOUT", "30"))
|
|
157
165
|
NAPALM_USERNAME = os.getenv("NAUTOBOT_NAPALM_USERNAME", "")
|
|
158
166
|
|
|
167
|
+
# Expiration date (YYYY-MM-DD) for an active Nautobot support contract with Network to Code.
|
|
168
|
+
# Displayed in the About page.
|
|
169
|
+
if (
|
|
170
|
+
"NAUTOBOT_NTC_SUPPORT_CONTRACT_EXPIRATION_DATE" in os.environ
|
|
171
|
+
and os.environ["NAUTOBOT_NTC_SUPPORT_CONTRACT_EXPIRATION_DATE"] != ""
|
|
172
|
+
):
|
|
173
|
+
NTC_SUPPORT_CONTRACT_EXPIRATION_DATE = datetime.date.fromisoformat(
|
|
174
|
+
os.environ["NAUTOBOT_NTC_SUPPORT_CONTRACT_EXPIRATION_DATE"]
|
|
175
|
+
)
|
|
176
|
+
|
|
159
177
|
# Default number of objects to display per page of the UI and REST API. Default is 50
|
|
160
178
|
if "NAUTOBOT_PAGINATE_COUNT" in os.environ and os.environ["NAUTOBOT_PAGINATE_COUNT"] != "":
|
|
161
179
|
PAGINATE_COUNT = int(os.environ["NAUTOBOT_PAGINATE_COUNT"])
|
|
@@ -404,6 +422,11 @@ SPECTACULAR_SETTINGS = {
|
|
|
404
422
|
# enum naming encountered a non-optimally resolvable collision for fields named "protocol".
|
|
405
423
|
"InterfaceRedundancyGroupProtocolChoices": "nautobot.dcim.choices.InterfaceRedundancyGroupProtocolChoices",
|
|
406
424
|
"ServiceProtocolChoices": "nautobot.ipam.choices.ServiceProtocolChoices",
|
|
425
|
+
# These choice enums need to be overridden because they get assigned to the `mode` field and
|
|
426
|
+
# result in this error:
|
|
427
|
+
# enum naming encountered a non-optimally resolvable collision for fields named "mode".
|
|
428
|
+
"InterfaceModeChoices": "nautobot.dcim.choices.InterfaceModeChoices",
|
|
429
|
+
"WirelessNetworkModeChoices": "nautobot.wireless.choices.WirelessNetworkModeChoices",
|
|
407
430
|
},
|
|
408
431
|
# Create separate schema components for PATCH requests (fields generally are not `required` on PATCH)
|
|
409
432
|
"COMPONENT_SPLIT_PATCH": True,
|
|
@@ -553,6 +576,7 @@ INSTALLED_APPS = [
|
|
|
553
576
|
"nautobot.tenancy",
|
|
554
577
|
"nautobot.users",
|
|
555
578
|
"nautobot.virtualization",
|
|
579
|
+
"nautobot.wireless",
|
|
556
580
|
"drf_spectacular",
|
|
557
581
|
"drf_spectacular_sidecar",
|
|
558
582
|
"graphene_django",
|
|
@@ -583,6 +607,7 @@ MIDDLEWARE = [
|
|
|
583
607
|
"nautobot.core.middleware.RemoteUserMiddleware",
|
|
584
608
|
"nautobot.core.middleware.ExternalAuthMiddleware",
|
|
585
609
|
"nautobot.core.middleware.ObjectChangeMiddleware",
|
|
610
|
+
"nautobot.core.middleware.UserDefinedTimeZoneMiddleware",
|
|
586
611
|
"django_prometheus.middleware.PrometheusAfterMiddleware",
|
|
587
612
|
]
|
|
588
613
|
|
|
@@ -707,6 +732,13 @@ CONSTANCE_ADDITIONAL_FIELDS = {
|
|
|
707
732
|
"required": False,
|
|
708
733
|
},
|
|
709
734
|
],
|
|
735
|
+
"optional_date_field": [
|
|
736
|
+
"django.forms.DateField",
|
|
737
|
+
{
|
|
738
|
+
"widget": "nautobot.core.forms.widgets.DatePicker",
|
|
739
|
+
"required": False,
|
|
740
|
+
},
|
|
741
|
+
],
|
|
710
742
|
}
|
|
711
743
|
|
|
712
744
|
CONSTANCE_CONFIG = {
|
|
@@ -761,13 +793,13 @@ CONSTANCE_CONFIG = {
|
|
|
761
793
|
field_type=bool,
|
|
762
794
|
),
|
|
763
795
|
"MAX_PAGE_SIZE": ConstanceConfigItem(
|
|
764
|
-
default=
|
|
796
|
+
default=_MAX_PAGE_SIZE_DEFAULT,
|
|
765
797
|
help_text="Maximum number of objects that a user can list in one UI page or one API call.\n"
|
|
766
798
|
"If set to 0, a user can retrieve an unlimited number of objects.",
|
|
767
799
|
field_type=int,
|
|
768
800
|
),
|
|
769
801
|
"PAGINATE_COUNT": ConstanceConfigItem(
|
|
770
|
-
default=
|
|
802
|
+
default=_PAGINATE_COUNT_DEFAULT,
|
|
771
803
|
help_text="Default number of objects to display per page when listing objects in the UI and/or REST API.",
|
|
772
804
|
field_type=int,
|
|
773
805
|
),
|
|
@@ -792,6 +824,12 @@ CONSTANCE_CONFIG = {
|
|
|
792
824
|
# Use custom field type defined above
|
|
793
825
|
field_type="optional_json_field",
|
|
794
826
|
),
|
|
827
|
+
"NTC_SUPPORT_CONTRACT_EXPIRATION_DATE": ConstanceConfigItem(
|
|
828
|
+
default="",
|
|
829
|
+
help_text="Expiration date for an active Nautobot support contract with Network to Code. "
|
|
830
|
+
"This value is displayed in the About page to provide additional support information.",
|
|
831
|
+
field_type="optional_date_field",
|
|
832
|
+
),
|
|
795
833
|
"PREFER_IPV4": ConstanceConfigItem(
|
|
796
834
|
default=False,
|
|
797
835
|
help_text="Whether to prefer IPv4 primary addresses over IPv6 primary addresses for devices.",
|
|
@@ -845,7 +883,7 @@ CONSTANCE_CONFIG_FIELDSETS = {
|
|
|
845
883
|
"RACK_ELEVATION_UNIT_TWO_DIGIT_FORMAT",
|
|
846
884
|
],
|
|
847
885
|
"Release Checking": ["RELEASE_CHECK_URL", "RELEASE_CHECK_TIMEOUT"],
|
|
848
|
-
"User Interface": ["SUPPORT_MESSAGE"],
|
|
886
|
+
"User Interface": ["SUPPORT_MESSAGE", "NTC_SUPPORT_CONTRACT_EXPIRATION_DATE"],
|
|
849
887
|
"Debugging": ["ALLOW_REQUEST_PROFILING"],
|
|
850
888
|
}
|
|
851
889
|
|
|
@@ -1113,3 +1151,29 @@ def silk_request_logging_intercept_logic(request):
|
|
|
1113
1151
|
|
|
1114
1152
|
|
|
1115
1153
|
SILKY_INTERCEPT_FUNC = silk_request_logging_intercept_logic
|
|
1154
|
+
|
|
1155
|
+
#
|
|
1156
|
+
# Kubernetes settings variables
|
|
1157
|
+
#
|
|
1158
|
+
|
|
1159
|
+
# Host of the kubernetes pod created in the kubernetes cluster
|
|
1160
|
+
KUBERNETES_DEFAULT_SERVICE_ADDRESS = os.getenv("NAUTOBOT_KUBERNETES_DEFAULT_SERVICE_ADDRESS", "")
|
|
1161
|
+
|
|
1162
|
+
# A dictionary that stores the kubernetes pod manifest used to create a job pod in the kubernetes cluster
|
|
1163
|
+
KUBERNETES_JOB_MANIFEST = {}
|
|
1164
|
+
|
|
1165
|
+
# Name of the kubernetes pod created in the kubernetes cluster
|
|
1166
|
+
KUBERNETES_JOB_POD_NAME = os.getenv("NAUTOBOT_KUBERNETES_JOB_POD_NAME", "")
|
|
1167
|
+
|
|
1168
|
+
# Namespace of the kubernetes pod created in the kubernetes cluster
|
|
1169
|
+
KUBERNETES_JOB_POD_NAMESPACE = os.getenv("NAUTOBOT_KUBERNETES_JOB_POD_NAMESPACE", "")
|
|
1170
|
+
|
|
1171
|
+
# File path to the SSL CA CERT used for authentication to create the job and job pod
|
|
1172
|
+
KUBERNETES_SSL_CA_CERT_PATH = os.getenv(
|
|
1173
|
+
"NAUTOBOT_KUBERNETES_SSL_CA_CERT_PATH", "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
|
1174
|
+
)
|
|
1175
|
+
|
|
1176
|
+
# File path to the Bearer token used for authentication to create the job and job pod
|
|
1177
|
+
KUBERNETES_TOKEN_PATH = os.getenv(
|
|
1178
|
+
"NAUTOBOT_KUBERNETES_TOKEN_PATH", "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
|
1179
|
+
)
|