nautobot 2.2.9__py3-none-any.whl → 2.3.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/apps/forms.py +4 -0
- nautobot/apps/models.py +10 -1
- nautobot/circuits/__init__.py +0 -1
- nautobot/circuits/apps.py +1 -0
- nautobot/circuits/factory.py +15 -3
- nautobot/circuits/filters.py +13 -0
- nautobot/circuits/forms.py +13 -0
- nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
- nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
- nautobot/circuits/models.py +16 -3
- nautobot/circuits/tables.py +16 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
- nautobot/circuits/tests/test_api.py +7 -5
- nautobot/circuits/tests/test_filters.py +12 -5
- nautobot/circuits/tests/test_models.py +33 -2
- nautobot/circuits/views.py +2 -3
- nautobot/cloud/__init__.py +0 -0
- nautobot/cloud/api/__init__.py +0 -0
- nautobot/cloud/api/serializers.py +54 -0
- nautobot/cloud/api/urls.py +16 -0
- nautobot/cloud/api/views.py +48 -0
- nautobot/cloud/apps.py +13 -0
- nautobot/cloud/factory.py +113 -0
- nautobot/cloud/filters.py +187 -0
- nautobot/cloud/forms.py +339 -0
- nautobot/cloud/homepage.py +43 -0
- nautobot/cloud/migrations/0001_initial.py +304 -0
- nautobot/cloud/migrations/__init__.py +0 -0
- nautobot/cloud/models.py +246 -0
- nautobot/cloud/navigation.py +85 -0
- nautobot/cloud/tables.py +157 -0
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +122 -0
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +111 -0
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +69 -0
- nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
- nautobot/cloud/tests/__init__.py +0 -0
- nautobot/cloud/tests/test_api.py +248 -0
- nautobot/cloud/tests/test_filters.py +125 -0
- nautobot/cloud/tests/test_models.py +43 -0
- nautobot/cloud/tests/test_views.py +153 -0
- nautobot/cloud/urls.py +14 -0
- nautobot/cloud/views.py +181 -0
- nautobot/core/__init__.py +0 -3
- nautobot/core/api/metadata.py +1 -0
- nautobot/core/api/parsers.py +7 -1
- nautobot/core/api/urls.py +1 -0
- nautobot/core/api/utils.py +1 -0
- nautobot/core/api/views.py +4 -0
- nautobot/core/apps/__init__.py +6 -3
- nautobot/core/constants.py +8 -0
- nautobot/core/factory.py +32 -1
- nautobot/core/filters.py +95 -13
- nautobot/core/forms/fields.py +10 -4
- nautobot/core/forms/forms.py +11 -3
- nautobot/core/forms/widgets.py +18 -1
- nautobot/core/graphql/schema.py +26 -4
- nautobot/core/jobs/__init__.py +16 -2
- nautobot/core/jobs/cleanup.py +100 -0
- nautobot/core/jobs/groups.py +38 -0
- nautobot/core/management/commands/generate_test_data.py +116 -3
- nautobot/core/models/__init__.py +34 -9
- nautobot/core/models/generics.py +19 -3
- nautobot/core/models/name_color_content_types.py +7 -28
- nautobot/core/models/querysets.py +4 -3
- nautobot/core/models/tree_queries.py +1 -1
- nautobot/core/models/utils.py +21 -5
- nautobot/core/settings.py +2 -17
- nautobot/core/settings.yaml +34 -13
- nautobot/core/settings_funcs.py +103 -0
- nautobot/core/tables.py +130 -56
- nautobot/core/templates/admin/search_form.html +1 -1
- nautobot/core/templates/buttons/add.html +11 -3
- nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
- nautobot/core/templates/buttons/export.html +101 -53
- nautobot/core/templates/buttons/job_import.html +11 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
- nautobot/core/templates/generic/object_bulk_update.html +3 -1
- nautobot/core/templates/generic/object_changelog.html +0 -9
- nautobot/core/templates/generic/object_list.html +156 -17
- nautobot/core/templates/generic/object_retrieve.html +80 -16
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
- nautobot/core/templates/inc/javascript.html +2 -0
- nautobot/core/templates/inc/media.html +2 -2
- nautobot/core/templates/inc/nav_menu.html +1 -0
- nautobot/core/templates/inc/paginator.html +7 -7
- nautobot/core/templates/inc/search_panel.html +2 -2
- nautobot/core/templates/inc/table.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +13 -8
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
- nautobot/core/templates/utilities/theme_preview.html +25 -8
- nautobot/core/templates/utilities/worker_status.html +152 -0
- nautobot/core/templatetags/buttons.py +335 -38
- nautobot/core/templatetags/form_helpers.py +1 -1
- nautobot/core/templatetags/helpers.py +181 -11
- nautobot/core/testing/api.py +5 -4
- nautobot/core/testing/filters.py +63 -14
- nautobot/core/testing/mixins.py +46 -0
- nautobot/core/testing/models.py +22 -0
- nautobot/core/testing/schema.py +4 -8
- nautobot/core/testing/views.py +31 -14
- nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
- nautobot/core/tests/integration/test_swagger.py +1 -1
- nautobot/core/tests/nautobot_config.py +0 -1
- nautobot/core/tests/runner.py +2 -2
- nautobot/core/tests/test_api.py +1 -0
- nautobot/core/tests/test_authentication.py +7 -2
- nautobot/core/tests/test_filters.py +11 -9
- nautobot/core/tests/test_forms.py +9 -0
- nautobot/core/tests/test_graphql.py +27 -16
- nautobot/core/tests/test_jobs.py +122 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +12 -5
- nautobot/core/tests/test_utils.py +31 -20
- nautobot/core/tests/test_views.py +6 -6
- nautobot/core/urls.py +8 -3
- nautobot/core/utils/deprecation.py +29 -0
- nautobot/core/utils/filtering.py +12 -9
- nautobot/core/utils/lookup.py +37 -2
- nautobot/core/utils/requests.py +4 -1
- nautobot/core/views/__init__.py +137 -24
- nautobot/core/views/generic.py +119 -67
- nautobot/core/views/mixins.py +105 -36
- nautobot/core/views/paginator.py +9 -3
- nautobot/core/views/renderers.py +121 -56
- nautobot/core/views/utils.py +81 -1
- nautobot/dcim/__init__.py +0 -1
- nautobot/dcim/api/serializers.py +180 -44
- nautobot/dcim/api/urls.py +7 -3
- nautobot/dcim/api/views.py +53 -7
- nautobot/dcim/apps.py +3 -0
- nautobot/dcim/choices.py +25 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/factory.py +252 -18
- nautobot/dcim/filters/__init__.py +373 -193
- nautobot/dcim/filters/mixins.py +274 -1
- nautobot/dcim/forms.py +834 -121
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/homepage.py +1 -1
- nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
- nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
- nautobot/dcim/migrations/0061_module_models.py +862 -0
- nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
- nautobot/dcim/models/__init__.py +8 -0
- nautobot/dcim/models/cables.py +15 -0
- nautobot/dcim/models/device_component_templates.py +207 -53
- nautobot/dcim/models/device_components.py +275 -99
- nautobot/dcim/models/devices.py +468 -13
- nautobot/dcim/models/racks.py +0 -1
- nautobot/dcim/navigation.py +47 -0
- nautobot/dcim/signals.py +3 -3
- nautobot/dcim/tables/__init__.py +35 -23
- nautobot/dcim/tables/devices.py +229 -43
- nautobot/dcim/tables/devicetypes.py +65 -9
- nautobot/dcim/tables/racks.py +5 -1
- nautobot/dcim/tables/template_code.py +46 -26
- nautobot/dcim/templates/dcim/cable_connect.html +76 -3
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/device/base.html +14 -6
- nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
- nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
- nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
- nautobot/dcim/templates/dcim/device/frontports.html +2 -3
- nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
- nautobot/dcim/templates/dcim/device/inventory.html +2 -3
- nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
- nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
- nautobot/dcim/templates/dcim/device/powerports.html +2 -3
- nautobot/dcim/templates/dcim/device/rearports.html +2 -3
- nautobot/dcim/templates/dcim/device.html +45 -1
- nautobot/dcim/templates/dcim/device_component.html +13 -5
- nautobot/dcim/templates/dcim/device_list.html +2 -1
- nautobot/dcim/templates/dcim/devicetype.html +99 -98
- nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
- nautobot/dcim/templates/dcim/interface.html +17 -2
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/manufacturer.html +24 -0
- nautobot/dcim/templates/dcim/module/base.html +97 -0
- nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
- nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
- nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
- nautobot/dcim/templates/dcim/module_destroy.html +5 -0
- nautobot/dcim/templates/dcim/module_frontports.html +53 -0
- nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
- nautobot/dcim/templates/dcim/module_list.html +20 -0
- nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
- nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
- nautobot/dcim/templates/dcim/module_powerports.html +53 -0
- nautobot/dcim/templates/dcim/module_rearports.html +53 -0
- nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
- nautobot/dcim/templates/dcim/module_update.html +71 -0
- nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
- nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
- nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +159 -0
- nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
- nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
- nautobot/dcim/tests/test_api.py +693 -208
- nautobot/dcim/tests/test_filters.py +843 -217
- nautobot/dcim/tests/test_models.py +1072 -8
- nautobot/dcim/tests/test_views.py +1510 -341
- nautobot/dcim/urls.py +17 -2
- nautobot/dcim/utils.py +2 -3
- nautobot/dcim/views.py +1106 -116
- nautobot/extras/__init__.py +0 -1
- nautobot/extras/api/serializers.py +115 -3
- nautobot/extras/api/urls.py +12 -0
- nautobot/extras/api/views.py +66 -0
- nautobot/extras/apps.py +2 -2
- nautobot/extras/choices.py +43 -0
- nautobot/extras/context_managers.py +13 -8
- nautobot/extras/datasources/git.py +2 -0
- nautobot/extras/factory.py +460 -9
- nautobot/extras/filters/__init__.py +174 -3
- nautobot/extras/filters/mixins.py +46 -43
- nautobot/extras/forms/base.py +24 -5
- nautobot/extras/forms/forms.py +227 -8
- nautobot/extras/forms/mixins.py +93 -0
- nautobot/extras/graphql/types.py +23 -10
- nautobot/extras/homepage.py +14 -1
- nautobot/extras/management/__init__.py +1 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
- nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
- nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
- nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
- nautobot/extras/migrations/0111_metadata.py +162 -0
- nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
- nautobot/extras/migrations/0113_saved_views.py +77 -0
- nautobot/extras/models/__init__.py +15 -1
- nautobot/extras/models/change_logging.py +3 -3
- nautobot/extras/models/contacts.py +4 -0
- nautobot/extras/models/customfields.py +18 -3
- nautobot/extras/models/groups.py +389 -225
- nautobot/extras/models/jobs.py +6 -3
- nautobot/extras/models/metadata.py +441 -0
- nautobot/extras/models/mixins.py +72 -62
- nautobot/extras/models/models.py +118 -9
- nautobot/extras/models/relationships.py +9 -2
- nautobot/extras/models/tags.py +13 -2
- nautobot/extras/navigation.py +57 -0
- nautobot/extras/plugins/__init__.py +3 -1
- nautobot/extras/querysets.py +30 -66
- nautobot/extras/signals.py +95 -100
- nautobot/extras/tables.py +165 -12
- nautobot/extras/templates/extras/dynamicgroup.html +44 -15
- nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
- nautobot/extras/templates/extras/job.html +1 -1
- nautobot/extras/templates/extras/jobresult.html +61 -74
- nautobot/extras/templates/extras/metadatatype_create.html +89 -0
- nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
- nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
- nautobot/extras/templates/extras/objectchange_list.html +0 -12
- nautobot/extras/templates/extras/plugins_list.html +1 -3
- nautobot/extras/templates/extras/role_retrieve.html +48 -0
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
- nautobot/extras/tests/integration/test_customfields.py +1 -0
- nautobot/extras/tests/test_api.py +509 -23
- nautobot/extras/tests/test_changelog.py +20 -9
- nautobot/extras/tests/test_context_managers.py +22 -15
- nautobot/extras/tests/test_datasources.py +13 -1
- nautobot/extras/tests/test_dynamicgroups.py +201 -171
- nautobot/extras/tests/test_filters.py +211 -12
- nautobot/extras/tests/test_jobs.py +6 -6
- nautobot/extras/tests/test_models.py +501 -4
- nautobot/extras/tests/test_relationships.py +1 -0
- nautobot/extras/tests/test_views.py +565 -8
- nautobot/extras/tests/test_webhooks.py +1 -1
- nautobot/extras/urls.py +5 -0
- nautobot/extras/utils.py +51 -11
- nautobot/extras/views.py +542 -76
- nautobot/ipam/__init__.py +0 -1
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/factory.py +17 -19
- nautobot/ipam/filters.py +13 -0
- nautobot/ipam/forms.py +8 -4
- nautobot/ipam/graphql/types.py +2 -2
- nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
- nautobot/ipam/models.py +11 -8
- nautobot/ipam/querysets.py +1 -1
- nautobot/ipam/signals.py +4 -2
- nautobot/ipam/tables.py +5 -0
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/prefix.html +1 -0
- nautobot/ipam/tests/test_api.py +37 -18
- nautobot/ipam/tests/test_filters.py +26 -2
- nautobot/ipam/tests/test_models.py +6 -0
- nautobot/ipam/tests/test_querysets.py +1 -1
- nautobot/ipam/tests/test_views.py +3 -2
- nautobot/ipam/urls.py +2 -2
- nautobot/ipam/views.py +18 -26
- nautobot/project-static/css/base.css +20 -0
- nautobot/project-static/css/dark.css +11 -0
- nautobot/project-static/docs/404.html +892 -88
- nautobot/project-static/docs/apps/index.html +892 -88
- nautobot/project-static/docs/apps/nautobot-apps.html +892 -88
- nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +919 -120
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +904 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1618 -903
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +935 -144
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +977 -188
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +901 -99
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +897 -93
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +991 -193
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +974 -131
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1078 -272
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1242 -334
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1727 -875
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1164 -381
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2088 -1374
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2246 -1422
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +912 -111
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +963 -163
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1010 -223
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1913 -1277
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1846 -1102
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +904 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2331 -1699
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1802 -1024
- nautobot/project-static/docs/development/apps/api/configuration-view.html +892 -88
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/global-search.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/graphql.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/index.html +942 -90
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +892 -88
- nautobot/project-static/docs/development/apps/api/prometheus.html +892 -88
- nautobot/project-static/docs/development/apps/api/setup.html +892 -88
- nautobot/project-static/docs/development/apps/api/testing.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/base-template.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/notes.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/urls.html +892 -88
- nautobot/project-static/docs/development/apps/index.html +892 -88
- nautobot/project-static/docs/development/apps/migration/code-updates.html +892 -88
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +892 -88
- nautobot/project-static/docs/development/apps/migration/from-v1.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +892 -88
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +892 -88
- nautobot/project-static/docs/development/core/application-registry.html +892 -88
- nautobot/project-static/docs/development/core/best-practices.html +893 -88
- nautobot/project-static/docs/development/core/bootstrap-ui.html +892 -88
- nautobot/project-static/docs/development/core/caching.html +892 -88
- nautobot/project-static/docs/development/core/controllers.html +892 -88
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +892 -88
- nautobot/project-static/docs/development/core/generic-views.html +892 -88
- nautobot/project-static/docs/development/core/getting-started.html +892 -88
- nautobot/project-static/docs/development/core/homepage.html +892 -88
- nautobot/project-static/docs/development/core/index.html +892 -88
- nautobot/project-static/docs/development/core/model-checklist.html +901 -89
- nautobot/project-static/docs/development/core/model-features.html +892 -88
- nautobot/project-static/docs/development/core/natural-keys.html +892 -88
- nautobot/project-static/docs/development/core/navigation-menu.html +892 -88
- nautobot/project-static/docs/development/core/release-checklist.html +895 -91
- nautobot/project-static/docs/development/core/role-internals.html +892 -88
- nautobot/project-static/docs/development/core/settings.html +892 -88
- nautobot/project-static/docs/development/core/style-guide.html +893 -89
- nautobot/project-static/docs/development/core/templates.html +904 -89
- nautobot/project-static/docs/development/core/testing.html +892 -88
- nautobot/project-static/docs/development/core/user-preferences.html +892 -88
- nautobot/project-static/docs/development/index.html +892 -88
- nautobot/project-static/docs/development/jobs/index.html +893 -89
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +892 -88
- nautobot/project-static/docs/index.html +892 -88
- nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
- nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
- nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
- nautobot/project-static/docs/models/dcim/module.html +15 -0
- nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
- nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
- nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
- nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
- nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
- nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
- nautobot/project-static/docs/models/extras/role.html +15 -0
- nautobot/project-static/docs/models/extras/savedview.html +15 -0
- nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
- nautobot/project-static/docs/models/extras/status.html +15 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +900 -89
- nautobot/project-static/docs/overview/design_philosophy.html +892 -88
- nautobot/project-static/docs/release-notes/index.html +1129 -92
- nautobot/project-static/docs/release-notes/version-1.0.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.1.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.2.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.3.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.4.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.5.html +893 -89
- nautobot/project-static/docs/release-notes/version-1.6.html +893 -89
- nautobot/project-static/docs/release-notes/version-2.0.html +892 -88
- nautobot/project-static/docs/release-notes/version-2.1.html +892 -88
- nautobot/project-static/docs/release-notes/version-2.2.html +895 -91
- nautobot/project-static/docs/release-notes/version-2.3.html +9954 -0
- nautobot/project-static/docs/requirements.txt +5 -5
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +331 -256
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +992 -174
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +896 -88
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +944 -153
- nautobot/project-static/docs/user-guide/administration/installation/index.html +901 -93
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +934 -122
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +954 -157
- nautobot/project-static/docs/user-guide/administration/installation/services.html +913 -112
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +908 -99
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +892 -88
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +892 -88
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +892 -88
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +893 -89
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +893 -89
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +896 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +895 -91
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +918 -100
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +913 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +921 -117
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +914 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +936 -118
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +928 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +937 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +928 -110
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +921 -117
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +923 -115
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +916 -112
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +940 -83
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +943 -86
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +921 -103
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +929 -125
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +922 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +936 -88
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +897 -89
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +897 -89
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +901 -96
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +897 -89
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +892 -88
- nautobot/project-static/docs/user-guide/index.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1258 -785
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +896 -88
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +950 -121
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +892 -88
- nautobot/project-static/js/forms.js +71 -0
- nautobot/project-static/js/table_sorting_indicator.js +46 -0
- nautobot/project-static/js/tableconfig.js +6 -1
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
- nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
- nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
- nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
- nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
- nautobot/tenancy/__init__.py +0 -1
- nautobot/tenancy/apps.py +1 -0
- nautobot/tenancy/factory.py +3 -2
- nautobot/tenancy/filters/__init__.py +1 -0
- nautobot/tenancy/forms.py +1 -1
- nautobot/tenancy/templates/tenancy/tenant.html +24 -20
- nautobot/tenancy/views.py +11 -10
- nautobot/users/__init__.py +0 -1
- nautobot/users/api/serializers.py +1 -1
- nautobot/users/api/views.py +4 -2
- nautobot/users/apps.py +3 -2
- nautobot/users/factory.py +3 -3
- nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
- nautobot/users/models.py +12 -0
- nautobot/users/tests/test_filters.py +6 -3
- nautobot/users/urls.py +8 -0
- nautobot/virtualization/__init__.py +0 -1
- nautobot/virtualization/apps.py +1 -0
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/forms.py +11 -3
- nautobot/virtualization/graphql/types.py +2 -2
- nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
- nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
- nautobot/virtualization/models.py +0 -2
- nautobot/virtualization/tables.py +10 -3
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
- nautobot/virtualization/tests/test_api.py +9 -4
- nautobot/virtualization/tests/test_filters.py +22 -0
- nautobot/virtualization/tests/test_models.py +7 -3
- nautobot/virtualization/tests/test_views.py +19 -3
- nautobot/virtualization/urls.py +2 -2
- nautobot/virtualization/views.py +10 -32
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/METADATA +20 -18
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/RECORD +677 -557
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
- nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
- nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
- nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
- nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
- nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
- nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
- nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
- nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
- nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
- nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
- nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
- /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -7,9 +7,11 @@ from nautobot.tenancy.tables import TenantGroupTable
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TableTestCase(TestCase):
|
|
10
|
+
maxDiff = None
|
|
11
|
+
|
|
10
12
|
def _validate_sorted_tree_queryset_same_with_table_queryset(self, queryset, table_class, field_name):
|
|
11
13
|
with self.subTest(f"Assert sorting {table_class.__name__} on '{field_name}'"):
|
|
12
|
-
table = table_class(queryset, order_by=field_name)
|
|
14
|
+
table = table_class(queryset.with_tree_fields(), order_by=field_name)
|
|
13
15
|
table_queryset_data = table.data.data.values_list("pk", flat=True)
|
|
14
16
|
sorted_queryset = queryset.with_tree_fields().extra(order_by=[field_name]).values_list("pk", flat=True)
|
|
15
17
|
self.assertEqual(list(table_queryset_data), list(sorted_queryset))
|
|
@@ -22,14 +22,14 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
22
22
|
location.description = ""
|
|
23
23
|
location.save()
|
|
24
24
|
self.assertEqual(
|
|
25
|
-
helpers.hyperlinked_object(location), f'<a href="/dcim/locations/{location.pk}/">{location.
|
|
25
|
+
helpers.hyperlinked_object(location), f'<a href="/dcim/locations/{location.pk}/">{location.display}</a>'
|
|
26
26
|
)
|
|
27
27
|
# An object with get_absolute_url and a description gives a titled hyperlink
|
|
28
28
|
location.description = "An important location"
|
|
29
29
|
location.save()
|
|
30
30
|
self.assertEqual(
|
|
31
31
|
helpers.hyperlinked_object(location),
|
|
32
|
-
f'<a href="/dcim/locations/{location.pk}/" title="An important location">{location.
|
|
32
|
+
f'<a href="/dcim/locations/{location.pk}/" title="An important location">{location.display}</a>',
|
|
33
33
|
)
|
|
34
34
|
# Optionally you can request a field other than the object's display string
|
|
35
35
|
self.assertEqual(
|
|
@@ -114,6 +114,13 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
114
114
|
)
|
|
115
115
|
self.assertEqual('"I am UTF-8! 😀"', helpers.render_json("I am UTF-8! 😀", False))
|
|
116
116
|
|
|
117
|
+
def test_render_uptime(self):
|
|
118
|
+
self.assertEqual(helpers.render_uptime(1024768), "11 days 20 hours 39 minutes")
|
|
119
|
+
self.assertEqual(helpers.render_uptime(""), helpers.placeholder(""))
|
|
120
|
+
self.assertEqual(helpers.render_uptime("123456"), "1 day 10 hours 17 minutes")
|
|
121
|
+
self.assertEqual(helpers.render_uptime(0), "0 days 0 hours 0 minutes")
|
|
122
|
+
self.assertEqual(helpers.render_uptime("foo bar"), helpers.placeholder("foo bar"))
|
|
123
|
+
|
|
117
124
|
def test_render_yaml(self):
|
|
118
125
|
self.assertEqual(
|
|
119
126
|
helpers.render_yaml({"syntax": "highlight"}), '<code class="language-yaml">syntax: highlight\n</code>'
|
|
@@ -251,7 +258,7 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
251
258
|
f'<span class="label" style="color: {fbcolor}; background-color: #{color}">{display}</span>',
|
|
252
259
|
)
|
|
253
260
|
# Assert when obj is None
|
|
254
|
-
self.assertEqual(helpers.hyperlinked_object_with_color(obj=None), "
|
|
261
|
+
self.assertEqual(helpers.hyperlinked_object_with_color(obj=None), '<span class="text-muted">—</span>')
|
|
255
262
|
|
|
256
263
|
@override_settings(BANNER_TOP="¡Hola, mundo!")
|
|
257
264
|
@override_config(example_app__SAMPLE_VARIABLE="Testing")
|
|
@@ -296,14 +303,14 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
296
303
|
location.save()
|
|
297
304
|
self.assertEqual(
|
|
298
305
|
helpers.hyperlinked_object_target_new_tab(location),
|
|
299
|
-
f'<a href="/dcim/locations/{location.pk}/" target="_blank" rel="noreferrer">{location.
|
|
306
|
+
f'<a href="/dcim/locations/{location.pk}/" target="_blank" rel="noreferrer">{location.display}</a>',
|
|
300
307
|
)
|
|
301
308
|
# An object with get_absolute_url and a description gives a titled hyperlink
|
|
302
309
|
location.description = "An important location"
|
|
303
310
|
location.save()
|
|
304
311
|
self.assertEqual(
|
|
305
312
|
helpers.hyperlinked_object_target_new_tab(location),
|
|
306
|
-
f'<a href="/dcim/locations/{location.pk}/" title="An important location" target="_blank" rel="noreferrer">{location.
|
|
313
|
+
f'<a href="/dcim/locations/{location.pk}/" title="An important location" target="_blank" rel="noreferrer">{location.display}</a>',
|
|
307
314
|
)
|
|
308
315
|
# Optionally you can request a field other than the object's display string
|
|
309
316
|
self.assertEqual(
|
|
@@ -7,12 +7,12 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
7
7
|
from django.core.exceptions import ValidationError
|
|
8
8
|
from django.db.models import Q
|
|
9
9
|
from django.http import QueryDict
|
|
10
|
-
from django.test import TestCase
|
|
11
10
|
|
|
12
11
|
from nautobot.circuits import models as circuits_models
|
|
13
12
|
from nautobot.core import exceptions, forms, settings_funcs
|
|
14
13
|
from nautobot.core.api import utils as api_utils
|
|
15
14
|
from nautobot.core.models import fields as core_fields, utils as models_utils, validators
|
|
15
|
+
from nautobot.core.testing import TestCase
|
|
16
16
|
from nautobot.core.utils import data as data_utils, filtering, lookup, requests
|
|
17
17
|
from nautobot.core.utils.migrations import update_object_change_ct_for_replaced_models
|
|
18
18
|
from nautobot.dcim import filters as dcim_filters, forms as dcim_forms, models as dcim_models, tables
|
|
@@ -485,61 +485,61 @@ class LookupRelatedFunctionTest(TestCase):
|
|
|
485
485
|
location_fields = ["comments", "name", "contact_email", "physical_address", "shipping_address"]
|
|
486
486
|
for field_name in location_fields:
|
|
487
487
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, field_name)
|
|
488
|
-
self.
|
|
488
|
+
self.assertIsInstance(form_field, forms.MultiValueCharField)
|
|
489
489
|
|
|
490
490
|
device_fields = ["serial", "name"]
|
|
491
491
|
for field_name in device_fields:
|
|
492
492
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
|
|
493
|
-
self.
|
|
493
|
+
self.assertIsInstance(form_field, forms.MultiValueCharField)
|
|
494
494
|
|
|
495
495
|
with self.subTest("Test IntegerField"):
|
|
496
496
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "asn")
|
|
497
|
-
self.
|
|
497
|
+
self.assertIsInstance(form_field, django_forms.IntegerField)
|
|
498
498
|
|
|
499
499
|
device_fields = ["vc_position", "vc_priority"]
|
|
500
500
|
for field_name in device_fields:
|
|
501
501
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
|
|
502
|
-
self.
|
|
502
|
+
self.assertIsInstance(form_field, django_forms.IntegerField)
|
|
503
503
|
|
|
504
504
|
with self.subTest("Test DynamicModelMultipleChoiceField"):
|
|
505
505
|
location_fields = ["tenant", "status"]
|
|
506
506
|
for field_name in location_fields:
|
|
507
507
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, field_name)
|
|
508
|
-
self.
|
|
508
|
+
self.assertIsInstance(form_field, forms.DynamicModelMultipleChoiceField)
|
|
509
509
|
|
|
510
510
|
device_fields = ["cluster", "device_type", "location"]
|
|
511
511
|
for field_name in device_fields:
|
|
512
512
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
|
|
513
|
-
self.
|
|
513
|
+
self.assertIsInstance(form_field, forms.DynamicModelMultipleChoiceField)
|
|
514
514
|
|
|
515
515
|
location_fields = ["has_circuit_terminations", "has_devices"]
|
|
516
516
|
for field_name in location_fields:
|
|
517
517
|
with self.subTest("Test ChoiceField", model=dcim_models.Location, field_name=field_name):
|
|
518
518
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, field_name)
|
|
519
|
-
self.
|
|
520
|
-
self.
|
|
519
|
+
self.assertIsInstance(form_field, django_forms.ChoiceField)
|
|
520
|
+
self.assertIsInstance(form_field.widget, forms.StaticSelect2)
|
|
521
521
|
|
|
522
522
|
device_fields = ["has_console_ports", "has_interfaces", "local_config_context_data"]
|
|
523
523
|
for field_name in device_fields:
|
|
524
524
|
with self.subTest("Test ChoiceField", model=dcim_models.Device, field_name=field_name):
|
|
525
525
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, field_name)
|
|
526
|
-
self.
|
|
527
|
-
self.
|
|
526
|
+
self.assertIsInstance(form_field, django_forms.ChoiceField)
|
|
527
|
+
self.assertIsInstance(form_field.widget, forms.StaticSelect2)
|
|
528
528
|
|
|
529
529
|
with self.subTest("Test MultipleChoiceField"):
|
|
530
530
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, "face")
|
|
531
|
-
self.
|
|
531
|
+
self.assertIsInstance(form_field, django_forms.MultipleChoiceField)
|
|
532
532
|
|
|
533
533
|
with self.subTest("Test DateTimePicker"):
|
|
534
534
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "last_updated")
|
|
535
|
-
self.
|
|
535
|
+
self.assertIsInstance(form_field.widget, forms.DateTimePicker)
|
|
536
536
|
|
|
537
537
|
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, "last_updated")
|
|
538
|
-
self.
|
|
538
|
+
self.assertIsInstance(form_field.widget, forms.DateTimePicker)
|
|
539
539
|
|
|
540
540
|
with self.subTest("Test DatePicker"):
|
|
541
541
|
form_field = filtering.get_filterset_parameter_form_field(circuits_models.Circuit, "install_date")
|
|
542
|
-
self.
|
|
542
|
+
self.assertIsInstance(form_field.widget, forms.DatePicker)
|
|
543
543
|
|
|
544
544
|
with self.subTest("Test Invalid parameter"):
|
|
545
545
|
with self.assertRaises(exceptions.FilterSetFieldNotFound) as err:
|
|
@@ -548,16 +548,27 @@ class LookupRelatedFunctionTest(TestCase):
|
|
|
548
548
|
|
|
549
549
|
with self.subTest("Test Content types"):
|
|
550
550
|
form_field = filtering.get_filterset_parameter_form_field(extras_models.Status, "content_types")
|
|
551
|
-
self.
|
|
551
|
+
self.assertIsInstance(form_field, forms.MultipleContentTypeField)
|
|
552
552
|
|
|
553
553
|
# Assert total ContentTypes generated by form_field is == total `content_types` generated by TaggableClassesQuery
|
|
554
554
|
form_field = filtering.get_filterset_parameter_form_field(extras_models.Tag, "content_types")
|
|
555
|
-
self.
|
|
556
|
-
self.
|
|
555
|
+
self.assertIsInstance(form_field, forms.MultipleContentTypeField)
|
|
556
|
+
self.assertQuerysetEqualAndNotEmpty(form_field.queryset, extras_utils.TaggableClassesQuery().as_queryset())
|
|
557
557
|
|
|
558
558
|
form_field = filtering.get_filterset_parameter_form_field(extras_models.JobHook, "content_types")
|
|
559
|
-
self.
|
|
560
|
-
self.
|
|
559
|
+
self.assertIsInstance(form_field, forms.MultipleContentTypeField)
|
|
560
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
561
|
+
form_field.queryset, extras_utils.ChangeLoggedModelsQuery().as_queryset()
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
with self.subTest("Test prefers_id"):
|
|
565
|
+
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Device, "location")
|
|
566
|
+
self.assertEqual("id", form_field.to_field_name)
|
|
567
|
+
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "vlans")
|
|
568
|
+
self.assertEqual("id", form_field.to_field_name)
|
|
569
|
+
# Test prefers_id=False (default)
|
|
570
|
+
form_field = filtering.get_filterset_parameter_form_field(dcim_models.Location, "racks")
|
|
571
|
+
self.assertEqual("name", form_field.to_field_name)
|
|
561
572
|
|
|
562
573
|
def test_convert_querydict_to_factory_formset_dict(self):
|
|
563
574
|
location_filter_set = dcim_filters.LocationFilterSet()
|
|
@@ -480,9 +480,9 @@ class ErrorPagesTestCase(TestCase):
|
|
|
480
480
|
def test_500_default_support_message(self, mock_get):
|
|
481
481
|
"""Nautobot's custom 500 page should be used and should include a default support message."""
|
|
482
482
|
url = reverse("home")
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
483
|
+
self.client.raise_request_exception = False
|
|
484
|
+
response = self.client.get(url)
|
|
485
|
+
self.assertTemplateUsed(response, "500.html")
|
|
486
486
|
self.assertContains(response, "Network to Code", status_code=500)
|
|
487
487
|
response_content = response.content.decode(response.charset)
|
|
488
488
|
self.assertInHTML(
|
|
@@ -497,9 +497,9 @@ class ErrorPagesTestCase(TestCase):
|
|
|
497
497
|
def test_500_custom_support_message(self, mock_get):
|
|
498
498
|
"""Nautobot's custom 500 page should be used and should include a custom support message if defined."""
|
|
499
499
|
url = reverse("home")
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
500
|
+
self.client.raise_request_exception = False
|
|
501
|
+
response = self.client.get(url)
|
|
502
|
+
self.assertTemplateUsed(response, "500.html")
|
|
503
503
|
self.assertNotContains(response, "Network to Code", status_code=500)
|
|
504
504
|
response_content = response.content.decode(response.charset)
|
|
505
505
|
self.assertInHTML("Hello world!", response_content)
|
nautobot/core/urls.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
|
-
from django.
|
|
3
|
-
from django.urls import path
|
|
2
|
+
from django.urls import include, path
|
|
4
3
|
from django.views.generic import TemplateView
|
|
5
4
|
from django.views.static import serve
|
|
6
5
|
|
|
@@ -13,6 +12,7 @@ from nautobot.core.views import (
|
|
|
13
12
|
SearchView,
|
|
14
13
|
StaticMediaFailureView,
|
|
15
14
|
ThemePreviewView,
|
|
15
|
+
WorkerStatusView,
|
|
16
16
|
)
|
|
17
17
|
from nautobot.extras.plugins.urls import (
|
|
18
18
|
apps_patterns,
|
|
@@ -32,11 +32,14 @@ urlpatterns = [
|
|
|
32
32
|
path("logout/", LogoutView.as_view(), name="logout"),
|
|
33
33
|
# Apps
|
|
34
34
|
path("circuits/", include("nautobot.circuits.urls")),
|
|
35
|
+
path("cloud/", include("nautobot.cloud.urls")),
|
|
35
36
|
path("dcim/", include("nautobot.dcim.urls")),
|
|
36
37
|
path("extras/", include("nautobot.extras.urls")),
|
|
37
38
|
path("ipam/", include("nautobot.ipam.urls")),
|
|
38
39
|
path("tenancy/", include("nautobot.tenancy.urls")),
|
|
40
|
+
# TODO: deprecate this url and use users
|
|
39
41
|
path("user/", include("nautobot.users.urls")),
|
|
42
|
+
path("users/", include("nautobot.users.urls", "users")),
|
|
40
43
|
path("virtualization/", include("nautobot.virtualization.urls")),
|
|
41
44
|
# API
|
|
42
45
|
path("api/", include("nautobot.core.api.urls")),
|
|
@@ -57,12 +60,14 @@ urlpatterns = [
|
|
|
57
60
|
# django-health-check
|
|
58
61
|
path(r"health/", include("health_check.urls")),
|
|
59
62
|
# FileProxy attachments download/get URLs used in admin views only
|
|
60
|
-
|
|
63
|
+
path(
|
|
61
64
|
"files/download/",
|
|
62
65
|
get_file_with_authorization,
|
|
63
66
|
{"add_attachment_headers": True},
|
|
64
67
|
name="db_file_storage.download_file",
|
|
65
68
|
),
|
|
69
|
+
# Celery worker status page
|
|
70
|
+
path("worker-status/", WorkerStatusView.as_view(), name="worker_status"),
|
|
66
71
|
# Templated css file
|
|
67
72
|
path(
|
|
68
73
|
"template.css", TemplateView.as_view(template_name="template.css", content_type="text/css"), name="template_css"
|
|
@@ -45,3 +45,32 @@ def class_deprecated(message):
|
|
|
45
45
|
def class_deprecated_in_favor_of(replacement_class):
|
|
46
46
|
"""Decorator to mark a class as deprecated and suggest a replacement class if it is subclassed from."""
|
|
47
47
|
return class_deprecated(f"please migrate your code to inherit from class {replacement_class.__name__} instead")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def method_deprecated(message):
|
|
51
|
+
"""Decorator to mark a method as deprecated with a custom message about what to call instead."""
|
|
52
|
+
|
|
53
|
+
def decorate(method):
|
|
54
|
+
def decorated_method(*args, **kwargs):
|
|
55
|
+
warnings.warn(
|
|
56
|
+
f"Method `{method.__name__}` is deprecated, and will be removed in a future Nautobot release. "
|
|
57
|
+
f"{message}",
|
|
58
|
+
DeprecationWarning,
|
|
59
|
+
stacklevel=2,
|
|
60
|
+
)
|
|
61
|
+
if LOG_DEPRECATION_WARNINGS:
|
|
62
|
+
logger.warning(
|
|
63
|
+
f"Method `{method.__name__}` is deprecated, and will be removed in a future Nautobot release. "
|
|
64
|
+
f"{message}",
|
|
65
|
+
stacklevel=2,
|
|
66
|
+
)
|
|
67
|
+
return method(*args, **kwargs)
|
|
68
|
+
|
|
69
|
+
return decorated_method
|
|
70
|
+
|
|
71
|
+
return decorate
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def method_deprecated_in_favor_of(replacement_method):
|
|
75
|
+
"""Decorator to mark a method as deprecated and suggest a replacement method instead."""
|
|
76
|
+
return method_deprecated(f"Please migrate your code to call `{replacement_method.__name__}` instead.")
|
nautobot/core/utils/filtering.py
CHANGED
|
@@ -96,8 +96,8 @@ def get_filterset_parameter_form_field(model, parameter, filterset=None):
|
|
|
96
96
|
StaticSelect2Multiple,
|
|
97
97
|
)
|
|
98
98
|
from nautobot.dcim.models import Device
|
|
99
|
-
from nautobot.extras.filters import ContentTypeMultipleChoiceFilter, CustomFieldFilterMixin
|
|
100
|
-
from nautobot.extras.models import ConfigContext
|
|
99
|
+
from nautobot.extras.filters import ContentTypeMultipleChoiceFilter, CustomFieldFilterMixin
|
|
100
|
+
from nautobot.extras.models import ConfigContext
|
|
101
101
|
from nautobot.extras.utils import ChangeLoggedModelsQuery, RoleModelsQuery, TaggableClassesQuery
|
|
102
102
|
from nautobot.virtualization.models import VirtualMachine
|
|
103
103
|
|
|
@@ -114,17 +114,18 @@ def get_filterset_parameter_form_field(model, parameter, filterset=None):
|
|
|
114
114
|
elif isinstance(field, NumberFilter):
|
|
115
115
|
form_field = forms.IntegerField()
|
|
116
116
|
elif isinstance(field, ModelMultipleChoiceFilter):
|
|
117
|
-
|
|
117
|
+
if getattr(field, "prefers_id", False):
|
|
118
|
+
to_field_name = "id"
|
|
119
|
+
else:
|
|
120
|
+
to_field_name = field.extra.get("to_field_name", "id")
|
|
118
121
|
form_attr = {
|
|
119
|
-
"queryset":
|
|
120
|
-
"to_field_name":
|
|
122
|
+
"queryset": field.extra["queryset"],
|
|
123
|
+
"to_field_name": to_field_name,
|
|
124
|
+
"query_params": field.extra.get("query_params", {}),
|
|
121
125
|
}
|
|
122
126
|
# ConfigContext requires content_type set to Device and VirtualMachine
|
|
123
127
|
if model == ConfigContext:
|
|
124
128
|
form_attr["query_params"] = {"content_types": [Device._meta.label_lower, VirtualMachine._meta.label_lower]}
|
|
125
|
-
# Status and Tag api requires content_type, to limit result to only related content_types
|
|
126
|
-
elif related_model in [Role, Status, Tag]:
|
|
127
|
-
form_attr["query_params"] = {"content_types": model._meta.label_lower}
|
|
128
129
|
|
|
129
130
|
form_field = DynamicModelMultipleChoiceField(**form_attr)
|
|
130
131
|
elif isinstance(
|
|
@@ -136,7 +137,9 @@ def get_filterset_parameter_form_field(model, parameter, filterset=None):
|
|
|
136
137
|
plural_name = slugify_dashes_to_underscores(model._meta.verbose_name_plural)
|
|
137
138
|
# Cable-connectable models use "cable_terminations", not "cables", as the feature name
|
|
138
139
|
if plural_name == "cables":
|
|
139
|
-
plural_name
|
|
140
|
+
plural_name = "cable_terminations"
|
|
141
|
+
elif plural_name == "metadata_types":
|
|
142
|
+
plural_name = "metadata"
|
|
140
143
|
try:
|
|
141
144
|
form_field = MultipleContentTypeField(choices_as_strings=True, feature=plural_name)
|
|
142
145
|
except KeyError:
|
nautobot/core/utils/lookup.py
CHANGED
|
@@ -208,6 +208,40 @@ def get_view_for_model(model, view_type=""):
|
|
|
208
208
|
return result
|
|
209
209
|
|
|
210
210
|
|
|
211
|
+
def get_model_for_view_name(view_name):
|
|
212
|
+
"""
|
|
213
|
+
Return the model class associated with the given view_name e.g. "circuits:circuit_detail", "dcim:device_list" and etc.
|
|
214
|
+
If the app_label or model_name contained by the given view_name is invalid, this will return `None`.
|
|
215
|
+
"""
|
|
216
|
+
app_label, model_name = view_name.split(":") # dcim, device_list
|
|
217
|
+
model_name = model_name.split("_")[0] # device
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
model = apps.get_model(app_label=app_label, model_name=model_name)
|
|
221
|
+
return model
|
|
222
|
+
except LookupError:
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def get_table_class_string_from_view_name(view_name):
|
|
227
|
+
"""Return the name of the TableClass name associated with the view_name
|
|
228
|
+
|
|
229
|
+
e.g. returns `LocationTable` for view_name `dcim:location_list`
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
view_name (String): The name of the view e.g. dcim:location_list, circuits:circuit_list
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
table_class_name (String): The name of the model table class or None e.g. LocationTable, CircuitTable
|
|
236
|
+
"""
|
|
237
|
+
model = get_model_for_view_name(view_name)
|
|
238
|
+
if model:
|
|
239
|
+
table_class = get_table_for_model(model)
|
|
240
|
+
if table_class:
|
|
241
|
+
return table_class.__name__
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
|
|
211
245
|
def get_created_and_last_updated_usernames_for_model(instance):
|
|
212
246
|
"""
|
|
213
247
|
Args:
|
|
@@ -224,8 +258,9 @@ def get_created_and_last_updated_usernames_for_model(instance):
|
|
|
224
258
|
created_by = None
|
|
225
259
|
last_updated_by = None
|
|
226
260
|
try:
|
|
227
|
-
created_by_record = object_change_records.
|
|
228
|
-
|
|
261
|
+
created_by_record = object_change_records.filter(action=ObjectChangeActionChoices.ACTION_CREATE).first()
|
|
262
|
+
if created_by_record is not None:
|
|
263
|
+
created_by = created_by_record.user_name
|
|
229
264
|
except ObjectChange.DoesNotExist:
|
|
230
265
|
pass
|
|
231
266
|
|
nautobot/core/utils/requests.py
CHANGED
|
@@ -65,7 +65,10 @@ def convert_querydict_to_factory_formset_acceptable_querydict(request_querydict,
|
|
|
65
65
|
# the filterset can handle the QueryDict conversion and we can just pass the QueryDict to the filterset
|
|
66
66
|
# then use the FilterSet to de-dupe the field names
|
|
67
67
|
lookup_field = re.sub(r"__\w+", "", filter_field_name)
|
|
68
|
-
|
|
68
|
+
if isinstance(request_querydict, QueryDict):
|
|
69
|
+
lookup_value = request_querydict.getlist(filter_field_name)
|
|
70
|
+
else:
|
|
71
|
+
lookup_value = request_querydict.get(filter_field_name)
|
|
69
72
|
|
|
70
73
|
query_dict.setlistdefault(lookup_field_placeholder % num, [lookup_field])
|
|
71
74
|
query_dict.setlistdefault(lookup_type_placeholder % num, [filter_field_name])
|
nautobot/core/views/__init__.py
CHANGED
|
@@ -7,14 +7,15 @@ import time
|
|
|
7
7
|
from db_file_storage.views import get_file
|
|
8
8
|
from django.apps import apps
|
|
9
9
|
from django.conf import settings
|
|
10
|
+
from django.contrib import messages
|
|
10
11
|
from django.contrib.auth.decorators import permission_required
|
|
11
|
-
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
|
|
12
|
+
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin, UserPassesTestMixin
|
|
12
13
|
from django.contrib.contenttypes.models import ContentType
|
|
13
14
|
from django.http import HttpResponseForbidden, HttpResponseServerError, JsonResponse
|
|
14
15
|
from django.shortcuts import get_object_or_404, render
|
|
15
16
|
from django.template import loader, RequestContext, Template
|
|
16
17
|
from django.template.exceptions import TemplateDoesNotExist
|
|
17
|
-
from django.urls import resolve, reverse
|
|
18
|
+
from django.urls import NoReverseMatch, resolve, reverse
|
|
18
19
|
from django.utils.encoding import smart_str
|
|
19
20
|
from django.views.csrf import csrf_failure as _csrf_failure
|
|
20
21
|
from django.views.decorators.csrf import requires_csrf_token
|
|
@@ -37,6 +38,7 @@ from rest_framework.response import Response
|
|
|
37
38
|
from rest_framework.versioning import AcceptHeaderVersioning
|
|
38
39
|
from rest_framework.views import APIView
|
|
39
40
|
|
|
41
|
+
from nautobot.core.celery import app
|
|
40
42
|
from nautobot.core.constants import SEARCH_MAX_RESULTS
|
|
41
43
|
from nautobot.core.forms import SearchForm
|
|
42
44
|
from nautobot.core.releases import get_latest_release
|
|
@@ -125,6 +127,112 @@ class HomeView(AccessMixin, TemplateView):
|
|
|
125
127
|
return self.render_to_response(context)
|
|
126
128
|
|
|
127
129
|
|
|
130
|
+
class WorkerStatusView(UserPassesTestMixin, TemplateView):
|
|
131
|
+
template_name = "utilities/worker_status.html"
|
|
132
|
+
|
|
133
|
+
def test_func(self):
|
|
134
|
+
return self.request.user.is_staff
|
|
135
|
+
|
|
136
|
+
def get(self, request, *args, **kwargs):
|
|
137
|
+
from nautobot.extras.models import JobResult
|
|
138
|
+
from nautobot.extras.tables import JobResultTable
|
|
139
|
+
|
|
140
|
+
# Sort queues but move the default queue to the front so that it appears first
|
|
141
|
+
def sort_queues(queue):
|
|
142
|
+
return (queue != settings.CELERY_TASK_DEFAULT_QUEUE, queue) # False sorts before true in Python sorted
|
|
143
|
+
|
|
144
|
+
# Sort workers on hostname first, then worker name -- celery worker names are in the form name@hostname (celery@worker.example.com)
|
|
145
|
+
def sort_workers(worker):
|
|
146
|
+
return list(reversed(worker.split("@")))
|
|
147
|
+
|
|
148
|
+
# Use a long timeout to retrieve the initial list of workers
|
|
149
|
+
max_timeout = 60
|
|
150
|
+
timeout = request.GET.get("timeout", "5")
|
|
151
|
+
if not timeout.isdigit():
|
|
152
|
+
timeout = 5
|
|
153
|
+
elif int(timeout) > max_timeout:
|
|
154
|
+
timeout = max_timeout
|
|
155
|
+
else:
|
|
156
|
+
timeout = int(timeout)
|
|
157
|
+
celery_inspect = app.control.inspect(timeout=timeout)
|
|
158
|
+
|
|
159
|
+
# stats() returns a dict of {worker_name: stats_dict}
|
|
160
|
+
worker_stats = celery_inspect.stats()
|
|
161
|
+
|
|
162
|
+
if worker_stats:
|
|
163
|
+
# Set explicit list of workers to speed up subsequent queries
|
|
164
|
+
celery_inspect = app.control.inspect(list(worker_stats.keys()), timeout=5.0)
|
|
165
|
+
|
|
166
|
+
# active() returns a dict of {worker_name: [task_dict, task_dict, ...]}
|
|
167
|
+
active_tasks = celery_inspect.active() or {}
|
|
168
|
+
|
|
169
|
+
# reserved() returns a dict of {worker_name: [task_dict, task_dict, ...]}
|
|
170
|
+
reserved_tasks = celery_inspect.reserved() or {}
|
|
171
|
+
|
|
172
|
+
# active_queues() returns a dict of {worker_name: [queue_dict, queue_dict, ...]}
|
|
173
|
+
active_queues = celery_inspect.active_queues() or {}
|
|
174
|
+
else:
|
|
175
|
+
# No workers were found, default to empty dicts for all commands
|
|
176
|
+
worker_stats = active_tasks = reserved_tasks = active_queues = {}
|
|
177
|
+
|
|
178
|
+
workers = []
|
|
179
|
+
for worker_name in sorted(worker_stats, key=sort_workers):
|
|
180
|
+
worker_details = worker_stats[worker_name]
|
|
181
|
+
active_task_job_results = JobResult.objects.filter(
|
|
182
|
+
id__in=[task["id"] for task in active_tasks.get(worker_name, [])]
|
|
183
|
+
).only("name", "date_created")
|
|
184
|
+
reserved_task_job_results = JobResult.objects.filter(
|
|
185
|
+
id__in=[task["id"] for task in reserved_tasks.get(worker_name, [])]
|
|
186
|
+
).only("name", "date_created")
|
|
187
|
+
running_tasks_table = JobResultTable(
|
|
188
|
+
active_task_job_results, exclude=["actions", "job_model", "summary", "user", "status"]
|
|
189
|
+
)
|
|
190
|
+
pending_tasks_table = JobResultTable(
|
|
191
|
+
reserved_task_job_results, exclude=["actions", "job_model", "summary", "user", "status"]
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Sort the worker's queue list
|
|
195
|
+
queues = sorted([queue["name"] for queue in active_queues.get(worker_name, [])], key=sort_queues)
|
|
196
|
+
|
|
197
|
+
workers.append(
|
|
198
|
+
{
|
|
199
|
+
"hostname": worker_name,
|
|
200
|
+
"running_tasks_table": running_tasks_table,
|
|
201
|
+
"pending_tasks_table": pending_tasks_table,
|
|
202
|
+
"queues": queues,
|
|
203
|
+
"uptime": worker_details["uptime"],
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Count the number of workers per queue
|
|
208
|
+
queue_worker_count = {
|
|
209
|
+
settings.CELERY_TASK_DEFAULT_QUEUE: set(),
|
|
210
|
+
}
|
|
211
|
+
for worker_name, task_queue_list in active_queues.items():
|
|
212
|
+
distinct_queues = {q["name"] for q in task_queue_list}
|
|
213
|
+
for queue in distinct_queues:
|
|
214
|
+
queue_worker_count.setdefault(queue, set())
|
|
215
|
+
queue_worker_count[queue].add(worker_name)
|
|
216
|
+
|
|
217
|
+
# Sort the queue_worker_count dictionary by queue name, then by worker hostname/name
|
|
218
|
+
# Then make the default queue the first entry
|
|
219
|
+
queue_worker_count = {
|
|
220
|
+
queue_name: sorted(queue_worker_count[queue_name], key=sort_workers)
|
|
221
|
+
for queue_name in sorted(queue_worker_count, key=sort_queues)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
context = {
|
|
225
|
+
"worker_status": {
|
|
226
|
+
"max_timeout": max_timeout,
|
|
227
|
+
"queue_worker_count": queue_worker_count,
|
|
228
|
+
"timeout": timeout,
|
|
229
|
+
"workers": workers,
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return self.render_to_response(context)
|
|
234
|
+
|
|
235
|
+
|
|
128
236
|
class ThemePreviewView(LoginRequiredMixin, TemplateView):
|
|
129
237
|
template_name = "utilities/theme_preview.html"
|
|
130
238
|
|
|
@@ -132,6 +240,8 @@ class ThemePreviewView(LoginRequiredMixin, TemplateView):
|
|
|
132
240
|
return {
|
|
133
241
|
"content_type": ContentType.objects.get_for_model(Status),
|
|
134
242
|
"object": Status.objects.first(),
|
|
243
|
+
"verbose_name": Status.objects.all().model._meta.verbose_name,
|
|
244
|
+
"verbose_name_plural": Status.objects.all().model._meta.verbose_name_plural,
|
|
135
245
|
}
|
|
136
246
|
|
|
137
247
|
|
|
@@ -177,28 +287,31 @@ class SearchView(AccessMixin, View):
|
|
|
177
287
|
# corresponding to that URL, and finally the queryset, filterset, and table classes needed
|
|
178
288
|
# to find and display the model search results.
|
|
179
289
|
url = get_route_for_model(f"{label}.{modelname}", "list")
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
290
|
+
try:
|
|
291
|
+
view_func = resolve(reverse(url)).func
|
|
292
|
+
# For UIViewSet, view_func.cls gets what we need; for an ObjectListView, view_func.view_class is it.
|
|
293
|
+
view_or_viewset = getattr(view_func, "cls", getattr(view_func, "view_class", None))
|
|
294
|
+
queryset = view_or_viewset.queryset.restrict(request.user, "view")
|
|
295
|
+
# For a UIViewSet, .filterset_class, for an ObjectListView, .filterset.
|
|
296
|
+
filterset = getattr(view_or_viewset, "filterset_class", getattr(view_or_viewset, "filterset", None))
|
|
297
|
+
# For a UIViewSet, .table_class, for an ObjectListView, .table.
|
|
298
|
+
table = getattr(view_or_viewset, "table_class", getattr(view_or_viewset, "table", None))
|
|
299
|
+
|
|
300
|
+
# Construct the results table for this object type
|
|
301
|
+
filtered_queryset = filterset({"q": form.cleaned_data["q"]}, queryset=queryset).qs
|
|
302
|
+
table = table(filtered_queryset, orderable=False)
|
|
303
|
+
table.paginate(per_page=SEARCH_MAX_RESULTS)
|
|
304
|
+
|
|
305
|
+
if table.page:
|
|
306
|
+
results.append(
|
|
307
|
+
{
|
|
308
|
+
"name": queryset.model._meta.verbose_name_plural,
|
|
309
|
+
"table": table,
|
|
310
|
+
"url": f"{reverse(url)}?q={form.cleaned_data.get('q')}",
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
except NoReverseMatch:
|
|
314
|
+
messages.error(request, f'Missing URL "{url}" - unable to show search results for {modelname}.')
|
|
202
315
|
|
|
203
316
|
return render(
|
|
204
317
|
request,
|