nautobot 2.3.16__py3-none-any.whl → 2.4.0b1__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/api.py +0 -2
- nautobot/apps/config.py +32 -3
- nautobot/apps/events.py +19 -0
- nautobot/apps/exceptions.py +0 -2
- nautobot/apps/ui.py +44 -9
- nautobot/apps/utils.py +0 -8
- nautobot/apps/views.py +2 -0
- nautobot/circuits/navigation.py +0 -57
- nautobot/circuits/tables.py +1 -2
- 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 +143 -26
- nautobot/cloud/factory.py +4 -1
- nautobot/cloud/models.py +1 -1
- nautobot/cloud/tests/test_filters.py +5 -4
- nautobot/core/api/fields.py +5 -5
- nautobot/core/api/metadata.py +28 -256
- nautobot/core/api/pagination.py +3 -2
- nautobot/core/api/renderers.py +3 -0
- nautobot/core/api/serializers.py +24 -244
- nautobot/core/api/urls.py +3 -4
- nautobot/core/api/utils.py +0 -62
- nautobot/core/api/views.py +48 -158
- nautobot/core/apps/__init__.py +22 -578
- nautobot/core/celery/__init__.py +13 -0
- nautobot/core/celery/log.py +4 -4
- nautobot/core/celery/schedulers.py +48 -3
- 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/filters.py +16 -21
- nautobot/core/fixtures/user-data.json +59 -0
- nautobot/core/forms/fields.py +53 -8
- nautobot/core/forms/utils.py +2 -1
- nautobot/core/graphql/schema.py +3 -1
- nautobot/core/graphql/types.py +1 -1
- nautobot/core/jobs/__init__.py +4 -4
- nautobot/core/jobs/cleanup.py +13 -49
- nautobot/core/jobs/groups.py +1 -1
- nautobot/core/management/commands/generate_test_data.py +21 -0
- nautobot/core/management/commands/validate_models.py +1 -1
- nautobot/core/middleware.py +16 -0
- nautobot/core/models/__init__.py +1 -1
- nautobot/core/models/fields.py +11 -7
- nautobot/core/models/query_functions.py +2 -2
- nautobot/core/models/tree_queries.py +3 -6
- nautobot/core/settings.py +44 -7
- nautobot/core/settings.yaml +86 -8
- nautobot/core/tables.py +15 -65
- nautobot/core/tasks.py +1 -1
- 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/media.html +0 -3
- nautobot/core/templates/inc/nav_menu.html +1 -1
- nautobot/core/templates/inc/relationships_panel.html +1 -1
- nautobot/core/templates/nautobot_config.py.j2 +3 -3
- nautobot/core/templates/panel_table.html +12 -0
- nautobot/core/templates/search.html +0 -7
- nautobot/core/templates/utilities/render_jinja2.html +117 -0
- nautobot/core/templatetags/helpers.py +101 -12
- nautobot/core/templatetags/ui_framework.py +40 -0
- nautobot/core/testing/api.py +23 -128
- nautobot/core/testing/context.py +18 -0
- nautobot/core/testing/filters.py +41 -58
- nautobot/core/testing/mixins.py +2 -7
- nautobot/core/testing/views.py +25 -123
- nautobot/core/tests/integration/test_app_home.py +1 -0
- nautobot/core/tests/integration/test_app_navbar.py +1 -0
- nautobot/core/tests/integration/test_filters.py +2 -0
- nautobot/core/tests/integration/test_home.py +1 -0
- nautobot/core/tests/integration/test_navbar.py +1 -0
- nautobot/core/tests/integration/test_view_authentication.py +1 -2
- nautobot/core/tests/nautobot_config.py +198 -0
- nautobot/core/tests/runner.py +3 -3
- nautobot/core/tests/test_api.py +82 -201
- nautobot/core/tests/test_csv.py +3 -25
- nautobot/core/tests/test_events.py +214 -0
- nautobot/core/tests/test_jinja_filters.py +1 -0
- nautobot/core/tests/test_jobs.py +84 -13
- nautobot/core/tests/test_navigations.py +7 -241
- nautobot/core/tests/test_templatetags_helpers.py +16 -0
- nautobot/core/tests/test_ui.py +150 -0
- nautobot/core/tests/test_utils.py +0 -25
- nautobot/core/tests/test_views.py +123 -31
- 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 +279 -0
- nautobot/core/ui/object_detail.py +1841 -0
- nautobot/core/ui/utils.py +36 -0
- nautobot/core/urls.py +4 -9
- nautobot/core/utils/config.py +30 -3
- nautobot/core/utils/lookup.py +20 -13
- nautobot/core/views/__init__.py +6 -1
- nautobot/core/views/generic.py +47 -52
- nautobot/core/views/mixins.py +15 -25
- nautobot/core/views/paginator.py +8 -5
- nautobot/core/views/renderers.py +3 -3
- nautobot/core/views/utils.py +11 -0
- nautobot/core/wsgi.py +3 -3
- nautobot/dcim/api/serializers.py +80 -179
- nautobot/dcim/api/urls.py +5 -0
- nautobot/dcim/api/views.py +17 -4
- nautobot/dcim/apps.py +1 -0
- nautobot/dcim/choices.py +28 -0
- nautobot/dcim/factory.py +58 -0
- nautobot/dcim/filters/__init__.py +197 -24
- nautobot/dcim/forms.py +203 -12
- nautobot/dcim/graphql/types.py +2 -2
- 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/models/__init__.py +4 -0
- nautobot/dcim/models/device_component_templates.py +2 -2
- nautobot/dcim/models/device_components.py +20 -22
- nautobot/dcim/models/devices.py +173 -4
- nautobot/dcim/models/locations.py +3 -3
- nautobot/dcim/models/power.py +5 -6
- nautobot/dcim/models/racks.py +6 -6
- nautobot/dcim/navigation.py +25 -224
- nautobot/dcim/signals.py +44 -0
- nautobot/dcim/tables/__init__.py +5 -3
- nautobot/dcim/tables/devices.py +96 -2
- nautobot/dcim/tables/devicetypes.py +2 -2
- nautobot/dcim/templates/dcim/controller/base.html +10 -0
- nautobot/dcim/templates/dcim/controller_create.html +1 -0
- nautobot/dcim/templates/dcim/controller_retrieve.html +5 -1
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +66 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +46 -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 +3 -1
- nautobot/dcim/templates/dcim/interface.html +1 -0
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/locationtype.html +0 -107
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +76 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +34 -0
- nautobot/dcim/tests/test_api.py +172 -61
- nautobot/dcim/tests/test_filters.py +171 -109
- nautobot/dcim/tests/test_forms.py +2 -51
- nautobot/dcim/tests/test_graphql.py +0 -52
- nautobot/dcim/tests/test_models.py +126 -4
- nautobot/dcim/tests/test_signals.py +1 -0
- nautobot/dcim/tests/test_views.py +103 -11
- nautobot/dcim/urls.py +72 -27
- nautobot/dcim/utils.py +2 -2
- nautobot/dcim/views.py +369 -62
- nautobot/extras/api/customfields.py +2 -2
- nautobot/extras/api/serializers.py +91 -75
- nautobot/extras/api/urls.py +4 -0
- nautobot/extras/api/views.py +78 -15
- nautobot/extras/choices.py +13 -0
- nautobot/extras/constants.py +0 -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 +59 -0
- nautobot/extras/forms/forms.py +125 -30
- nautobot/extras/forms/mixins.py +3 -11
- nautobot/extras/graphql/types.py +25 -1
- nautobot/extras/group_sync.py +3 -3
- nautobot/extras/health_checks.py +2 -1
- nautobot/extras/jobs.py +62 -26
- 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/managers.py +1 -3
- nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
- 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/models/__init__.py +4 -0
- nautobot/extras/models/change_logging.py +7 -3
- nautobot/extras/models/customfields.py +11 -12
- nautobot/extras/models/groups.py +9 -13
- nautobot/extras/models/jobs.py +218 -37
- nautobot/extras/models/models.py +2 -2
- 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 +56 -32
- nautobot/extras/plugins/marketplace_manifest.yml +450 -0
- nautobot/extras/plugins/urls.py +1 -0
- nautobot/extras/plugins/views.py +48 -1
- nautobot/extras/signals.py +39 -1
- nautobot/extras/tables.py +40 -6
- 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 +36 -6
- nautobot/extras/templates/extras/job_edit.html +5 -2
- nautobot/extras/templates/extras/job_list.html +2 -7
- nautobot/extras/templates/extras/jobqueue_retrieve.html +44 -0
- nautobot/extras/templates/extras/marketplace.html +278 -0
- nautobot/extras/templates/extras/plugins_list.html +35 -1
- nautobot/extras/templates/extras/plugins_tiles.html +79 -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/api_test_job.py +1 -1
- nautobot/extras/test_jobs/atomic_transaction.py +2 -2
- nautobot/extras/test_jobs/dry_run.py +1 -1
- nautobot/extras/test_jobs/fail.py +5 -5
- nautobot/extras/test_jobs/file_output.py +1 -1
- nautobot/extras/test_jobs/file_upload_fail.py +1 -1
- nautobot/extras/test_jobs/file_upload_pass.py +1 -1
- nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
- nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
- nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
- nautobot/extras/test_jobs/log_redaction.py +1 -1
- nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
- nautobot/extras/test_jobs/modify_db.py +1 -1
- nautobot/extras/test_jobs/object_var_optional.py +1 -1
- nautobot/extras/test_jobs/object_var_required.py +1 -1
- nautobot/extras/test_jobs/object_vars.py +1 -1
- nautobot/extras/test_jobs/pass.py +3 -3
- nautobot/extras/test_jobs/profiling.py +1 -1
- nautobot/extras/test_jobs/relative_import.py +3 -3
- nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
- nautobot/extras/test_jobs/task_queues.py +1 -1
- nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
- nautobot/extras/tests/test_api.py +157 -55
- nautobot/extras/tests/test_context_managers.py +4 -1
- nautobot/extras/tests/test_customfields.py +1 -1
- nautobot/extras/tests/test_datasources.py +1 -2
- nautobot/extras/tests/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/test_filters.py +219 -535
- nautobot/extras/tests/test_forms.py +1 -20
- nautobot/extras/tests/test_job_variables.py +73 -152
- nautobot/extras/tests/test_jobs.py +43 -54
- nautobot/extras/tests/test_models.py +71 -16
- nautobot/extras/tests/test_relationships.py +5 -2
- nautobot/extras/tests/test_utils.py +23 -2
- nautobot/extras/tests/test_views.py +183 -43
- nautobot/extras/tests/test_webhooks.py +2 -1
- nautobot/extras/urls.py +2 -20
- nautobot/extras/utils.py +118 -4
- nautobot/extras/views.py +203 -92
- nautobot/extras/webhooks.py +5 -2
- nautobot/ipam/api/fields.py +3 -3
- nautobot/ipam/api/serializers.py +36 -137
- nautobot/ipam/api/views.py +93 -62
- nautobot/ipam/lookups.py +62 -101
- nautobot/ipam/models.py +11 -63
- nautobot/ipam/navigation.py +0 -90
- nautobot/ipam/querysets.py +2 -2
- nautobot/ipam/tables.py +6 -20
- nautobot/ipam/templates/ipam/routetarget.html +0 -28
- nautobot/ipam/templates/ipam/vrf.html +0 -47
- nautobot/ipam/tests/test_api.py +8 -419
- nautobot/ipam/tests/test_filters.py +39 -119
- nautobot/ipam/tests/test_forms.py +47 -51
- nautobot/ipam/tests/test_migrations.py +30 -30
- nautobot/ipam/tests/test_models.py +0 -41
- nautobot/ipam/tests/test_querysets.py +1 -63
- nautobot/ipam/urls.py +3 -69
- nautobot/ipam/utils/__init__.py +0 -24
- nautobot/ipam/views.py +153 -198
- nautobot/project-static/css/base.css +38 -3
- nautobot/project-static/docs/404.html +421 -19
- nautobot/project-static/docs/apps/index.html +421 -19
- nautobot/project-static/docs/apps/nautobot-apps.html +421 -19
- nautobot/project-static/docs/assets/extra.css +5 -1
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +421 -172
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +425 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +424 -22
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9809 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +424 -63
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +457 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +425 -25
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +457 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +425 -215
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +430 -342
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5799 -1054
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +421 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +447 -176
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +460 -21
- nautobot/project-static/docs/development/apps/api/configuration-view.html +421 -19
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/global-search.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/graphql.html +421 -19
- nautobot/project-static/docs/development/apps/api/models/index.html +421 -19
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +421 -19
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +424 -41
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +421 -19
- nautobot/project-static/docs/development/apps/api/prometheus.html +421 -19
- nautobot/project-static/docs/development/apps/api/setup.html +425 -155
- nautobot/project-static/docs/development/apps/api/testing.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +421 -19
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +701 -130
- nautobot/project-static/docs/development/apps/api/views/base-template.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/index.html +423 -20
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +425 -19
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +451 -19
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/notes.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +421 -19
- nautobot/project-static/docs/development/apps/api/views/urls.html +421 -19
- nautobot/project-static/docs/development/apps/index.html +421 -19
- nautobot/project-static/docs/development/apps/migration/code-updates.html +422 -52
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +422 -20
- nautobot/project-static/docs/development/apps/migration/from-v1.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +421 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +424 -22
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9219 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9333 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9474 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9517 -0
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +424 -22
- nautobot/project-static/docs/development/core/application-registry.html +421 -19
- nautobot/project-static/docs/development/core/best-practices.html +421 -19
- nautobot/project-static/docs/development/core/bootstrap-ui.html +421 -19
- nautobot/project-static/docs/development/core/caching.html +421 -19
- nautobot/project-static/docs/development/core/controllers.html +423 -19
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +490 -45
- nautobot/project-static/docs/development/core/generic-views.html +421 -19
- nautobot/project-static/docs/development/core/getting-started.html +566 -179
- nautobot/project-static/docs/development/core/homepage.html +432 -30
- nautobot/project-static/docs/development/core/index.html +421 -19
- nautobot/project-static/docs/development/core/local-k8s.html +9453 -0
- nautobot/project-static/docs/development/core/model-checklist.html +424 -22
- nautobot/project-static/docs/development/core/model-features.html +421 -19
- nautobot/project-static/docs/development/core/natural-keys.html +421 -19
- nautobot/project-static/docs/development/core/navigation-menu.html +438 -26
- nautobot/project-static/docs/development/core/release-checklist.html +435 -45
- nautobot/project-static/docs/development/core/role-internals.html +421 -19
- nautobot/project-static/docs/development/core/settings.html +421 -19
- nautobot/project-static/docs/development/core/style-guide.html +421 -19
- nautobot/project-static/docs/development/core/templates.html +431 -22
- nautobot/project-static/docs/development/core/testing.html +421 -19
- nautobot/project-static/docs/development/core/ui-component-framework.html +11020 -0
- nautobot/project-static/docs/development/core/user-preferences.html +424 -22
- nautobot/project-static/docs/development/index.html +421 -19
- nautobot/project-static/docs/development/jobs/index.html +546 -160
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +421 -19
- nautobot/project-static/docs/index.html +421 -19
- 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/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/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 +426 -20
- nautobot/project-static/docs/overview/design_philosophy.html +421 -19
- nautobot/project-static/docs/release-notes/index.html +445 -22
- nautobot/project-static/docs/release-notes/version-1.0.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.1.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.2.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.3.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.4.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.5.html +421 -19
- nautobot/project-static/docs/release-notes/version-1.6.html +634 -667
- nautobot/project-static/docs/release-notes/version-2.0.html +421 -19
- nautobot/project-static/docs/release-notes/version-2.1.html +421 -19
- nautobot/project-static/docs/release-notes/version-2.2.html +421 -19
- nautobot/project-static/docs/release-notes/version-2.3.html +684 -886
- nautobot/project-static/docs/release-notes/version-2.4.html +10007 -0
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +334 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +421 -19
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +421 -19
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +423 -21
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +433 -32
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +421 -19
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +765 -180
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +434 -29
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +421 -19
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/index.html +426 -20
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +421 -19
- nautobot/project-static/docs/user-guide/administration/installation/services.html +421 -19
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +421 -19
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +442 -41
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +435 -66
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +435 -66
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +421 -19
- 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 +421 -19
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +427 -21
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +457 -20
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +447 -22
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9333 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +424 -22
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +421 -19
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +424 -22
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9271 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9175 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9169 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9235 -0
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +421 -19
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +421 -19
- nautobot/project-static/docs/user-guide/index.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +9575 -0
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +426 -20
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9182 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9250 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +424 -22
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +489 -56
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +421 -19
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +421 -19
- nautobot/project-static/img/jinja_logo.svg +97 -0
- nautobot/project-static/js/forms.js +5 -0
- nautobot/project-static/js/nav_menu.js +2 -1
- nautobot/tenancy/api/serializers.py +0 -2
- 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 +32 -23
- nautobot/users/admin.py +3 -1
- nautobot/users/api/serializers.py +4 -5
- nautobot/users/api/views.py +1 -1
- 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/serializers.py +4 -4
- nautobot/virtualization/filters.py +2 -20
- nautobot/virtualization/navigation.py +0 -48
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
- nautobot/virtualization/tests/test_filters.py +57 -183
- 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 +64 -0
- nautobot/wireless/tests/__init__.py +0 -0
- nautobot/wireless/tests/test_api.py +247 -0
- nautobot/wireless/tests/test_filters.py +54 -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 +129 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +11 -14
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +674 -551
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
- nautobot/core/utils/navigation.py +0 -54
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
nautobot/extras/models/jobs.py
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
4
|
from datetime import timedelta
|
|
5
|
+
import json
|
|
5
6
|
import logging
|
|
7
|
+
import signal
|
|
6
8
|
|
|
9
|
+
from billiard.exceptions import SoftTimeLimitExceeded
|
|
7
10
|
from celery.exceptions import NotRegistered
|
|
8
11
|
from celery.utils.log import get_logger, LoggingProxy
|
|
9
12
|
from django.conf import settings
|
|
@@ -26,12 +29,12 @@ from nautobot.core.celery import (
|
|
|
26
29
|
)
|
|
27
30
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
28
31
|
from nautobot.core.models import BaseManager, BaseModel
|
|
29
|
-
from nautobot.core.models.fields import JSONArrayField
|
|
30
32
|
from nautobot.core.models.generics import OrganizationalModel, PrimaryModel
|
|
31
33
|
from nautobot.core.utils.logging import sanitize
|
|
32
34
|
from nautobot.extras.choices import (
|
|
33
35
|
ButtonClassChoices,
|
|
34
36
|
JobExecutionType,
|
|
37
|
+
JobQueueTypeChoices,
|
|
35
38
|
JobResultStatusChoices,
|
|
36
39
|
LogLevelChoices,
|
|
37
40
|
)
|
|
@@ -49,6 +52,8 @@ from nautobot.extras.querysets import JobQuerySet, ScheduledJobExtendedQuerySet
|
|
|
49
52
|
from nautobot.extras.utils import (
|
|
50
53
|
ChangeLoggedModelsQuery,
|
|
51
54
|
extras_features,
|
|
55
|
+
get_job_queue_worker_count,
|
|
56
|
+
run_kubernetes_job_and_return_job_result,
|
|
52
57
|
)
|
|
53
58
|
|
|
54
59
|
from .customfields import CustomFieldModel
|
|
@@ -164,17 +169,26 @@ class Job(PrimaryModel):
|
|
|
164
169
|
help_text="Maximum runtime in seconds before the job will be forcibly terminated."
|
|
165
170
|
"<br>Set to 0 to use Nautobot system default",
|
|
166
171
|
)
|
|
167
|
-
task_queues = JSONArrayField(
|
|
168
|
-
base_field=models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True),
|
|
169
|
-
default=list,
|
|
170
|
-
blank=True,
|
|
171
|
-
help_text="Comma separated list of task queues that this job can run on. A blank list will use the default queue",
|
|
172
|
-
)
|
|
173
172
|
supports_dryrun = models.BooleanField(
|
|
174
173
|
default=False,
|
|
175
174
|
editable=False,
|
|
176
175
|
help_text="If supported, allows the job to bypass approval when running with dryrun argument set to true",
|
|
177
176
|
)
|
|
177
|
+
job_queues = models.ManyToManyField(
|
|
178
|
+
to="extras.JobQueue",
|
|
179
|
+
related_name="jobs",
|
|
180
|
+
verbose_name="Job Queues",
|
|
181
|
+
help_text="The job queues that this job can be run on",
|
|
182
|
+
through="extras.JobQueueAssignment",
|
|
183
|
+
)
|
|
184
|
+
default_job_queue = models.ForeignKey(
|
|
185
|
+
to="extras.JobQueue",
|
|
186
|
+
related_name="default_for_jobs",
|
|
187
|
+
on_delete=models.PROTECT,
|
|
188
|
+
verbose_name="Default Job Queue",
|
|
189
|
+
null=False,
|
|
190
|
+
blank=False,
|
|
191
|
+
)
|
|
178
192
|
|
|
179
193
|
# Flags to indicate whether the above properties are inherited from the source code or overridden by the database
|
|
180
194
|
grouping_override = models.BooleanField(
|
|
@@ -213,11 +227,14 @@ class Job(PrimaryModel):
|
|
|
213
227
|
default=False,
|
|
214
228
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
215
229
|
)
|
|
216
|
-
|
|
230
|
+
job_queues_override = models.BooleanField(
|
|
231
|
+
default=False,
|
|
232
|
+
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
233
|
+
)
|
|
234
|
+
default_job_queue_override = models.BooleanField(
|
|
217
235
|
default=False,
|
|
218
236
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
219
237
|
)
|
|
220
|
-
|
|
221
238
|
objects = BaseManager.from_queryset(JobQuerySet)()
|
|
222
239
|
|
|
223
240
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
@@ -234,13 +251,13 @@ class Job(PrimaryModel):
|
|
|
234
251
|
def __str__(self):
|
|
235
252
|
return self.name
|
|
236
253
|
|
|
237
|
-
def delete(self
|
|
254
|
+
def delete(self):
|
|
238
255
|
if self.module_name.startswith("nautobot."):
|
|
239
256
|
raise ProtectedError(
|
|
240
257
|
f"Unable to delete Job {self}. System Job cannot be deleted",
|
|
241
258
|
[],
|
|
242
259
|
)
|
|
243
|
-
super().delete(
|
|
260
|
+
super().delete()
|
|
244
261
|
|
|
245
262
|
@property
|
|
246
263
|
def job_class(self):
|
|
@@ -302,6 +319,38 @@ class Job(PrimaryModel):
|
|
|
302
319
|
except TypeError as err: # keep 2.0-2.2.2 exception behavior
|
|
303
320
|
raise NotRegistered from err
|
|
304
321
|
|
|
322
|
+
@property
|
|
323
|
+
def task_queues(self):
|
|
324
|
+
"""Deprecated backward-compatibility property for the list of queue names for this Job."""
|
|
325
|
+
return self.job_queues.values_list("name", flat=True)
|
|
326
|
+
|
|
327
|
+
@task_queues.setter
|
|
328
|
+
def task_queues(self, value):
|
|
329
|
+
job_queues = []
|
|
330
|
+
# value is going to be a comma separated list of queue names
|
|
331
|
+
if isinstance(value, str):
|
|
332
|
+
value = value.split(",")
|
|
333
|
+
for queue in value:
|
|
334
|
+
try:
|
|
335
|
+
job_queues.append(JobQueue.objects.get(name=queue))
|
|
336
|
+
except JobQueue.DoesNotExist:
|
|
337
|
+
raise ValidationError(f"Job Queue {queue} does not exist in the database.")
|
|
338
|
+
self.job_queues.set(job_queues)
|
|
339
|
+
|
|
340
|
+
@property
|
|
341
|
+
def task_queues_override(self):
|
|
342
|
+
return self.job_queues_override
|
|
343
|
+
|
|
344
|
+
@task_queues_override.setter
|
|
345
|
+
def task_queues_override(self, value):
|
|
346
|
+
if isinstance(value, bool):
|
|
347
|
+
raise ValidationError(
|
|
348
|
+
{
|
|
349
|
+
"task_queues_override": f"{value} is invalid for field task_queues_override, use a boolean value instead"
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
self.job_queues_override = value
|
|
353
|
+
|
|
305
354
|
def clean(self):
|
|
306
355
|
"""For any non-overridden fields, make sure they get reset to the actual underlying class value if known."""
|
|
307
356
|
from nautobot.extras.jobs import get_job
|
|
@@ -312,6 +361,9 @@ class Job(PrimaryModel):
|
|
|
312
361
|
if not getattr(self, f"{field_name}_override", False):
|
|
313
362
|
setattr(self, field_name, getattr(job_class, field_name))
|
|
314
363
|
|
|
364
|
+
if not self.job_queues_override:
|
|
365
|
+
self.task_queues = job_class.task_queues or [settings.CELERY_TASK_DEFAULT_QUEUE]
|
|
366
|
+
|
|
315
367
|
# Protect against invalid input when auto-creating Job records
|
|
316
368
|
if len(self.module_name) > JOB_MAX_NAME_LENGTH:
|
|
317
369
|
raise ValidationError(f"Module name may not exceed {JOB_MAX_NAME_LENGTH} characters in length")
|
|
@@ -478,6 +530,75 @@ class JobLogEntry(BaseModel):
|
|
|
478
530
|
verbose_name_plural = "job log entries"
|
|
479
531
|
|
|
480
532
|
|
|
533
|
+
#
|
|
534
|
+
# Job Queues
|
|
535
|
+
#
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@extras_features(
|
|
539
|
+
"custom_links",
|
|
540
|
+
"custom_validators",
|
|
541
|
+
"export_templates",
|
|
542
|
+
"graphql",
|
|
543
|
+
"webhooks",
|
|
544
|
+
)
|
|
545
|
+
class JobQueue(PrimaryModel):
|
|
546
|
+
"""
|
|
547
|
+
A Job Queue represents a structure that is used to manage, organize and schedule jobs for Nautobot workers.
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
|
|
551
|
+
description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
|
|
552
|
+
queue_type = models.CharField(
|
|
553
|
+
max_length=50,
|
|
554
|
+
choices=JobQueueTypeChoices,
|
|
555
|
+
)
|
|
556
|
+
tenant = models.ForeignKey(
|
|
557
|
+
to="tenancy.Tenant",
|
|
558
|
+
on_delete=models.PROTECT,
|
|
559
|
+
related_name="job_queues",
|
|
560
|
+
blank=True,
|
|
561
|
+
null=True,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
documentation_static_path = "docs/user-guide/platform-functionality/jobs/jobqueue.html"
|
|
565
|
+
|
|
566
|
+
class Meta:
|
|
567
|
+
ordering = ["name"]
|
|
568
|
+
|
|
569
|
+
def __str__(self):
|
|
570
|
+
return f"{self.queue_type}: {self.name}"
|
|
571
|
+
|
|
572
|
+
@property
|
|
573
|
+
def display(self):
|
|
574
|
+
worker_count = get_job_queue_worker_count(job_queue=self)
|
|
575
|
+
workers = "worker" if worker_count == 1 else "workers"
|
|
576
|
+
return f"{self.queue_type}: {self.name} ({worker_count} {workers})"
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
@extras_features(
|
|
580
|
+
"custom_links",
|
|
581
|
+
"custom_validators",
|
|
582
|
+
"export_templates",
|
|
583
|
+
"graphql",
|
|
584
|
+
)
|
|
585
|
+
class JobQueueAssignment(BaseModel):
|
|
586
|
+
"""
|
|
587
|
+
Through table model that represents the m2m relationship between jobs and job queues.
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name="job_queue_assignments")
|
|
591
|
+
job_queue = models.ForeignKey(JobQueue, on_delete=models.CASCADE, related_name="job_assignments")
|
|
592
|
+
is_metadata_associable_model = False
|
|
593
|
+
|
|
594
|
+
class Meta:
|
|
595
|
+
unique_together = ["job", "job_queue"]
|
|
596
|
+
ordering = ["job", "job_queue"]
|
|
597
|
+
|
|
598
|
+
def __str__(self):
|
|
599
|
+
return f"{self.job}: {self.job_queue}"
|
|
600
|
+
|
|
601
|
+
|
|
481
602
|
#
|
|
482
603
|
# Job results
|
|
483
604
|
#
|
|
@@ -610,13 +731,13 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
610
731
|
# Only add metrics if we have a related job model. If we are moving to a terminal state we should always
|
|
611
732
|
# have a related job model, so this shouldn't be too tight of a restriction.
|
|
612
733
|
if self.job_model:
|
|
613
|
-
duration = self.date_done - self.
|
|
734
|
+
duration = self.date_done - self.created
|
|
614
735
|
JOB_RESULT_METRIC.labels(self.job_model.grouping, self.job_model.name, status).observe(
|
|
615
736
|
duration.total_seconds()
|
|
616
737
|
)
|
|
617
738
|
|
|
618
739
|
@classmethod
|
|
619
|
-
def execute_job(cls, job_model, user, *job_args, celery_kwargs=None, profile=False, **job_kwargs):
|
|
740
|
+
def execute_job(cls, job_model, user, *job_args, celery_kwargs=None, profile=False, job_result=None, **job_kwargs):
|
|
620
741
|
"""
|
|
621
742
|
Create a JobResult instance and run a job in the current process, blocking until the job finishes.
|
|
622
743
|
|
|
@@ -628,6 +749,7 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
628
749
|
user (User): User object to link to the JobResult instance
|
|
629
750
|
celery_kwargs (dict, optional): Dictionary of kwargs to pass as **kwargs to Celery when job is run
|
|
630
751
|
profile (bool, optional): Whether to run cProfile on the job execution
|
|
752
|
+
job_result (JobResult, optional): Existing JobResult with status PENDING, used in kubernetes job execution
|
|
631
753
|
*job_args: positional args passed to the job task
|
|
632
754
|
**job_kwargs: keyword args passed to the job task
|
|
633
755
|
|
|
@@ -635,7 +757,14 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
635
757
|
JobResult instance
|
|
636
758
|
"""
|
|
637
759
|
return cls.enqueue_job(
|
|
638
|
-
job_model,
|
|
760
|
+
job_model,
|
|
761
|
+
user,
|
|
762
|
+
*job_args,
|
|
763
|
+
celery_kwargs=celery_kwargs,
|
|
764
|
+
profile=profile,
|
|
765
|
+
job_result=job_result,
|
|
766
|
+
synchronous=True,
|
|
767
|
+
**job_kwargs,
|
|
639
768
|
)
|
|
640
769
|
|
|
641
770
|
@classmethod
|
|
@@ -648,10 +777,11 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
648
777
|
profile=False,
|
|
649
778
|
schedule=None,
|
|
650
779
|
task_queue=None,
|
|
780
|
+
job_result=None,
|
|
651
781
|
synchronous=False,
|
|
652
782
|
**job_kwargs,
|
|
653
783
|
):
|
|
654
|
-
"""Create a JobResult instance and enqueue a job to be executed asynchronously by a Celery worker.
|
|
784
|
+
"""Create/Modify a JobResult instance and enqueue a job to be executed asynchronously by a Celery worker.
|
|
655
785
|
|
|
656
786
|
Args:
|
|
657
787
|
job_model (Job): The Job to be enqueued for execution.
|
|
@@ -660,6 +790,7 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
660
790
|
profile (bool, optional): If True, dump cProfile stats on the job execution.
|
|
661
791
|
schedule (ScheduledJob, optional): ScheduledJob instance to link to the JobResult. Cannot be used with synchronous=True.
|
|
662
792
|
task_queue (str, optional): The celery queue to send the job to. If not set, use the default celery queue.
|
|
793
|
+
job_result (JobResult, optional): Existing JobResult with status PENDING, to be modified and to be used in kubernetes job execution.
|
|
663
794
|
synchronous (bool, optional): If True, run the job in the current process, blocking until the job completes.
|
|
664
795
|
*job_args: positional args passed to the job task (UNUSED)
|
|
665
796
|
**job_kwargs: keyword args passed to the job task
|
|
@@ -672,15 +803,34 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
672
803
|
if schedule is not None and synchronous:
|
|
673
804
|
raise ValueError("Scheduled jobs cannot be run synchronously")
|
|
674
805
|
|
|
675
|
-
job_result
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
806
|
+
if job_result is None:
|
|
807
|
+
job_result = cls.objects.create(
|
|
808
|
+
name=job_model.name,
|
|
809
|
+
job_model=job_model,
|
|
810
|
+
scheduled_job=schedule,
|
|
811
|
+
user=user,
|
|
812
|
+
)
|
|
813
|
+
else:
|
|
814
|
+
if job_result.user != user:
|
|
815
|
+
raise ValueError(
|
|
816
|
+
f"There is a mismatch between the user specified {user} and the user associated with the job result {job_result.user}"
|
|
817
|
+
)
|
|
818
|
+
if job_result.job_model != job_model:
|
|
819
|
+
raise ValueError(
|
|
820
|
+
f"There is a mismatch between the job specified {job_model} and the job associated with the job result {job_result.job_model}"
|
|
821
|
+
)
|
|
681
822
|
|
|
682
823
|
if task_queue is None:
|
|
683
|
-
task_queue =
|
|
824
|
+
task_queue = job_model.default_job_queue.name
|
|
825
|
+
|
|
826
|
+
job_queue = JobQueue.objects.get(name=task_queue)
|
|
827
|
+
# Kubernetes Job Queue logic
|
|
828
|
+
# As we execute Kubernetes jobs, we want to execute `run_kubernetes_job_and_return_job_result`
|
|
829
|
+
# the first time the kubernetes job is enqueued to spin up the kubernetes pod.
|
|
830
|
+
# And from the kubernetes pod, we specify "--local"/synchronous=True
|
|
831
|
+
# so that `run_kubernetes_job_and_return_job_result` is not executed again and the job will be run locally.
|
|
832
|
+
if job_queue.queue_type == JobQueueTypeChoices.TYPE_KUBERNETES and not synchronous:
|
|
833
|
+
return run_kubernetes_job_and_return_job_result(job_queue, job_result, json.dumps(job_kwargs))
|
|
684
834
|
|
|
685
835
|
job_celery_kwargs = {
|
|
686
836
|
"nautobot_job_job_model_id": job_model.id,
|
|
@@ -712,12 +862,24 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
712
862
|
redirect_logger = get_logger("celery.redirected")
|
|
713
863
|
proxy = LoggingProxy(redirect_logger, app.conf.worker_redirect_stdouts_level)
|
|
714
864
|
with contextlib.redirect_stdout(proxy), contextlib.redirect_stderr(proxy):
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
)
|
|
865
|
+
|
|
866
|
+
def alarm_handler(*args, **kwargs):
|
|
867
|
+
raise SoftTimeLimitExceeded()
|
|
868
|
+
|
|
869
|
+
# Set alarm_handler to be called on a SIGALRM, and schedule a SIGALRM based on the soft time limit
|
|
870
|
+
signal.signal(signal.SIGALRM, alarm_handler)
|
|
871
|
+
signal.alarm(int(job_model.soft_time_limit) or settings.CELERY_TASK_SOFT_TIME_LIMIT)
|
|
872
|
+
|
|
873
|
+
try:
|
|
874
|
+
eager_result = run_job.apply(
|
|
875
|
+
args=[job_model.class_path, *job_args],
|
|
876
|
+
kwargs=job_kwargs,
|
|
877
|
+
task_id=str(job_result.id),
|
|
878
|
+
**job_celery_kwargs,
|
|
879
|
+
)
|
|
880
|
+
finally:
|
|
881
|
+
# Cancel the scheduled SIGALRM if it hasn't fired already
|
|
882
|
+
signal.alarm(0)
|
|
721
883
|
|
|
722
884
|
# copy fields from eager result to job result
|
|
723
885
|
job_result.refresh_from_db()
|
|
@@ -728,9 +890,15 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
728
890
|
"exc_message": sanitize(str(eager_result.result)),
|
|
729
891
|
}
|
|
730
892
|
else:
|
|
731
|
-
|
|
893
|
+
if eager_result.result is not None:
|
|
894
|
+
job_result.result = sanitize(eager_result.result)
|
|
895
|
+
else:
|
|
896
|
+
job_result.result = None
|
|
732
897
|
job_result.status = eager_result.status
|
|
733
|
-
|
|
898
|
+
if eager_result.traceback is not None:
|
|
899
|
+
job_result.traceback = sanitize(eager_result.traceback)
|
|
900
|
+
else:
|
|
901
|
+
job_result.traceback = None
|
|
734
902
|
job_result.date_done = timezone.now()
|
|
735
903
|
job_result.save()
|
|
736
904
|
else:
|
|
@@ -927,13 +1095,13 @@ class ScheduledJob(BaseModel):
|
|
|
927
1095
|
args = models.JSONField(blank=True, default=list, encoder=NautobotKombuJSONEncoder)
|
|
928
1096
|
kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
|
|
929
1097
|
celery_kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
|
|
930
|
-
|
|
931
|
-
|
|
1098
|
+
job_queue = models.ForeignKey(
|
|
1099
|
+
to="extras.JobQueue",
|
|
1100
|
+
on_delete=models.SET_NULL,
|
|
1101
|
+
related_name="scheduled_jobs",
|
|
1102
|
+
null=True,
|
|
932
1103
|
blank=True,
|
|
933
|
-
|
|
934
|
-
verbose_name="Queue Override",
|
|
935
|
-
help_text="Queue defined in CELERY_TASK_QUEUES. Leave empty for default queuing.",
|
|
936
|
-
db_index=True,
|
|
1104
|
+
verbose_name="Job Queue Override",
|
|
937
1105
|
)
|
|
938
1106
|
one_off = models.BooleanField(
|
|
939
1107
|
default=False,
|
|
@@ -1065,6 +1233,19 @@ class ScheduledJob(BaseModel):
|
|
|
1065
1233
|
|
|
1066
1234
|
return self.to_cron()
|
|
1067
1235
|
|
|
1236
|
+
@property
|
|
1237
|
+
def queue(self):
|
|
1238
|
+
"""Deprecated backward-compatibility property for the queue name this job is scheduled for."""
|
|
1239
|
+
return self.job_queue.name if self.job_queue else ""
|
|
1240
|
+
|
|
1241
|
+
@queue.setter
|
|
1242
|
+
def queue(self, value):
|
|
1243
|
+
if value:
|
|
1244
|
+
try:
|
|
1245
|
+
self.job_queue = JobQueue.objects.get(name=value)
|
|
1246
|
+
except JobQueue.DoesNotExist:
|
|
1247
|
+
raise ValidationError(f"Job Queue {value} does not exist in the database.")
|
|
1248
|
+
|
|
1068
1249
|
@staticmethod
|
|
1069
1250
|
def earliest_possible_time():
|
|
1070
1251
|
return timezone.now() + timedelta(seconds=15)
|
|
@@ -1180,7 +1361,7 @@ class ScheduledJob(BaseModel):
|
|
|
1180
1361
|
|
|
1181
1362
|
def to_cron(self):
|
|
1182
1363
|
tz = self.time_zone
|
|
1183
|
-
t = self.start_time.astimezone(tz)
|
|
1364
|
+
t = self.start_time.astimezone(tz)
|
|
1184
1365
|
if self.interval == JobExecutionType.TYPE_HOURLY:
|
|
1185
1366
|
return TzAwareCrontab(minute=t.minute, tz=tz)
|
|
1186
1367
|
elif self.interval == JobExecutionType.TYPE_DAILY:
|
nautobot/extras/models/models.py
CHANGED
|
@@ -207,7 +207,7 @@ class ConfigContextModel(models.Model, ConfigContextSchemaValidationMixin):
|
|
|
207
207
|
# Annotation not available, so fall back to manually querying for the config context
|
|
208
208
|
config_context_data = ConfigContext.objects.get_for_object(self).values_list("data", flat=True)
|
|
209
209
|
else:
|
|
210
|
-
config_context_data = self.config_context_data or []
|
|
210
|
+
config_context_data = self.config_context_data or []
|
|
211
211
|
config_context_data = [
|
|
212
212
|
c["data"] for c in sorted(config_context_data, key=lambda k: (k["weight"], k["name"]))
|
|
213
213
|
]
|
|
@@ -831,7 +831,7 @@ class Note(ChangeLoggedModel, BaseModel):
|
|
|
831
831
|
unique_together = [["assigned_object_type", "assigned_object_id", "user_name", "created"]]
|
|
832
832
|
|
|
833
833
|
def __str__(self):
|
|
834
|
-
return f"{self.assigned_object} - {self.created.isoformat() if self.created else None}"
|
|
834
|
+
return f"{self.assigned_object} - {self.created.isoformat() if self.created else None}"
|
|
835
835
|
|
|
836
836
|
def save(self, *args, **kwargs):
|
|
837
837
|
# Record the user's name as static strings
|
|
@@ -105,7 +105,7 @@ class RelationshipModel(models.Model):
|
|
|
105
105
|
|
|
106
106
|
# Determine if the relationship is applicable to this object based on the filter
|
|
107
107
|
# To resolve the filter we are using the FilterSet for the given model
|
|
108
|
-
# If there is no match when we query
|
|
108
|
+
# If there is no match when we query our id along with the filter
|
|
109
109
|
# Then the relationship is not applicable to this object
|
|
110
110
|
if getattr(relationship, f"{side}_filter"):
|
|
111
111
|
filterset = get_filterset_for_model(self._meta.model)
|
|
@@ -222,6 +222,74 @@ class RelationshipModel(models.Model):
|
|
|
222
222
|
"""
|
|
223
223
|
return self.get_relationships_data(advanced_ui=True)
|
|
224
224
|
|
|
225
|
+
def get_relationships_with_related_objects(self, include_hidden=False, advanced_ui=None):
|
|
226
|
+
"""Alternative version of get_relationships()."""
|
|
227
|
+
src_relationships, dst_relationships = Relationship.objects.get_for_model(self)
|
|
228
|
+
|
|
229
|
+
if advanced_ui is not None:
|
|
230
|
+
src_relationships = src_relationships.filter(advanced_ui=advanced_ui)
|
|
231
|
+
dst_relationships = dst_relationships.filter(advanced_ui=advanced_ui)
|
|
232
|
+
|
|
233
|
+
resp = {
|
|
234
|
+
RelationshipSideChoices.SIDE_SOURCE: {},
|
|
235
|
+
RelationshipSideChoices.SIDE_DESTINATION: {},
|
|
236
|
+
RelationshipSideChoices.SIDE_PEER: {},
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for side, relationships in (
|
|
240
|
+
(RelationshipSideChoices.SIDE_SOURCE, src_relationships),
|
|
241
|
+
(RelationshipSideChoices.SIDE_DESTINATION, dst_relationships),
|
|
242
|
+
):
|
|
243
|
+
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
244
|
+
for relationship in relationships:
|
|
245
|
+
if getattr(relationship, f"{side}_hidden") and not include_hidden:
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
# Determine if the relationship is applicable to this object based on the filter
|
|
249
|
+
# To resolve the filter we are using the FilterSet for the given model
|
|
250
|
+
# If there is no match when we query our id along with the filter
|
|
251
|
+
# Then the relationship is not applicable to this object
|
|
252
|
+
if getattr(relationship, f"{side}_filter"):
|
|
253
|
+
filterset = get_filterset_for_model(self._meta.model)
|
|
254
|
+
if filterset:
|
|
255
|
+
filter_params = getattr(relationship, f"{side}_filter")
|
|
256
|
+
if not filterset(filter_params, self._meta.model.objects.filter(id=self.id)).qs.exists():
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
# Construct the queryset for related objects for this relationship
|
|
260
|
+
remote_ct = getattr(relationship, f"{peer_side}_type")
|
|
261
|
+
remote_model = remote_ct.model_class()
|
|
262
|
+
if remote_model is not None:
|
|
263
|
+
if not relationship.symmetric:
|
|
264
|
+
query_params = {f"{peer_side}_for_associations__relationship": relationship.pk}
|
|
265
|
+
resp[side][relationship] = remote_model.objects.filter(**query_params)
|
|
266
|
+
if not relationship.has_many(peer_side):
|
|
267
|
+
resp[side][relationship] = resp[side][relationship].first()
|
|
268
|
+
else:
|
|
269
|
+
resp[RelationshipSideChoices.SIDE_PEER][relationship] = remote_model.objects.filter(
|
|
270
|
+
Q(source_for_associations__relationship=relationship.pk)
|
|
271
|
+
| Q(destination_for_associations__relationship=relationship.pk)
|
|
272
|
+
).exclude(pk=self.pk)
|
|
273
|
+
if not relationship.has_many(peer_side):
|
|
274
|
+
resp[side][relationship] = resp[side][relationship].first()
|
|
275
|
+
else:
|
|
276
|
+
# Maybe an uninstalled App?
|
|
277
|
+
# We can't provide a relevant queryset, but we can provide a descriptive string
|
|
278
|
+
if not relationship.symmetric:
|
|
279
|
+
count = RelationshipAssociation.objects.filter(
|
|
280
|
+
relationship=relationship, **{f"{side}_id": self.pk}
|
|
281
|
+
).count()
|
|
282
|
+
resp[side][relationship] = f"{count} {remote_ct} object(s)"
|
|
283
|
+
else:
|
|
284
|
+
count = (
|
|
285
|
+
RelationshipAssociation.objects.filter(relationship=relationship)
|
|
286
|
+
.filter(Q(source_id=self.pk) | Q(destination_id=self.pk))
|
|
287
|
+
.count()
|
|
288
|
+
)
|
|
289
|
+
resp[RelationshipSideChoices.SIDE_PEER][relationship] = f"{count} {remote_ct} object(s)"
|
|
290
|
+
|
|
291
|
+
return resp
|
|
292
|
+
|
|
225
293
|
@classmethod
|
|
226
294
|
def required_related_objects_errors(
|
|
227
295
|
cls, output_for="ui", initial_data=None, relationships_key_specified=False, instance=None
|
|
@@ -46,6 +46,11 @@ class Secret(PrimaryModel):
|
|
|
46
46
|
def __str__(self):
|
|
47
47
|
return self.name
|
|
48
48
|
|
|
49
|
+
def get_provider_display(self):
|
|
50
|
+
if provider := registry["secrets_providers"].get(self.provider):
|
|
51
|
+
return provider.name
|
|
52
|
+
return self.provider
|
|
53
|
+
|
|
49
54
|
def rendered_parameters(self, obj=None):
|
|
50
55
|
"""Render self.parameters as a Jinja2 template with the given object as context."""
|
|
51
56
|
try:
|