nautobot 2.2.8__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 +110 -14
- nautobot/core/forms/fields.py +10 -4
- nautobot/core/forms/forms.py +11 -3
- nautobot/core/forms/widgets.py +18 -1
- nautobot/core/graphql/generators.py +2 -2
- nautobot/core/graphql/schema.py +28 -7
- nautobot/core/jobs/__init__.py +20 -3
- 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 +15 -19
- nautobot/core/settings.yaml +48 -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 +28 -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_general_functionality.py +1 -1
- 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 +204 -2
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +12 -5
- nautobot/core/tests/test_templatetags_netutils.py +3 -3
- 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 +282 -99
- nautobot/dcim/models/devices.py +472 -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 +248 -47
- 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/deviceredundancygroup_retrieve.html +6 -0
- 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 +1103 -8
- nautobot/dcim/tests/test_views.py +1525 -343
- nautobot/dcim/urls.py +17 -2
- nautobot/dcim/utils.py +2 -3
- nautobot/dcim/views.py +1109 -113
- 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 +73 -59
- 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 +26 -3
- nautobot/extras/jobs.py +2 -2
- 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 +87 -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 +109 -101
- nautobot/extras/tables.py +201 -17
- 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/job_detail.html +11 -0
- 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 +586 -8
- nautobot/extras/tests/test_webhooks.py +1 -1
- nautobot/extras/urls.py +5 -0
- nautobot/extras/utils.py +85 -16
- nautobot/extras/views.py +562 -122
- 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 +20 -20
- 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 +9 -2
- 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 +20 -34
- nautobot/project-static/css/base.css +21 -0
- nautobot/project-static/css/dark.css +11 -0
- nautobot/project-static/docs/404.html +894 -90
- nautobot/project-static/docs/apps/index.html +894 -90
- nautobot/project-static/docs/apps/nautobot-apps.html +894 -90
- 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 +921 -122
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +906 -103
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1620 -905
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +937 -146
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +979 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +903 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +899 -95
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +993 -195
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +976 -133
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1080 -274
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1244 -336
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1729 -877
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1166 -383
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2090 -1376
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2248 -1424
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +914 -113
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +965 -165
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1012 -225
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1915 -1279
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1848 -1104
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +906 -103
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2335 -1701
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1804 -1026
- nautobot/project-static/docs/development/apps/api/configuration-view.html +894 -90
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/global-search.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/graphql.html +894 -90
- nautobot/project-static/docs/development/apps/api/models/index.html +944 -92
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +894 -90
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +894 -90
- nautobot/project-static/docs/development/apps/api/prometheus.html +894 -90
- nautobot/project-static/docs/development/apps/api/setup.html +894 -90
- nautobot/project-static/docs/development/apps/api/testing.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +894 -90
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/base-template.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/index.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/notes.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +894 -90
- nautobot/project-static/docs/development/apps/api/views/urls.html +894 -90
- nautobot/project-static/docs/development/apps/index.html +894 -90
- nautobot/project-static/docs/development/apps/migration/code-updates.html +894 -90
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +894 -90
- nautobot/project-static/docs/development/apps/migration/from-v1.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +894 -90
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +894 -90
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +894 -90
- nautobot/project-static/docs/development/core/application-registry.html +894 -90
- nautobot/project-static/docs/development/core/best-practices.html +895 -90
- nautobot/project-static/docs/development/core/bootstrap-ui.html +894 -90
- nautobot/project-static/docs/development/core/caching.html +894 -90
- nautobot/project-static/docs/development/core/controllers.html +894 -90
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +894 -90
- nautobot/project-static/docs/development/core/generic-views.html +894 -90
- nautobot/project-static/docs/development/core/getting-started.html +894 -90
- nautobot/project-static/docs/development/core/homepage.html +894 -90
- nautobot/project-static/docs/development/core/index.html +905 -90
- nautobot/project-static/docs/development/core/model-checklist.html +903 -91
- nautobot/project-static/docs/development/core/model-features.html +894 -90
- nautobot/project-static/docs/development/core/natural-keys.html +894 -90
- nautobot/project-static/docs/development/core/navigation-menu.html +894 -90
- nautobot/project-static/docs/development/core/release-checklist.html +897 -93
- nautobot/project-static/docs/development/core/role-internals.html +894 -90
- nautobot/project-static/docs/development/core/settings.html +894 -90
- nautobot/project-static/docs/development/core/style-guide.html +895 -91
- nautobot/project-static/docs/development/core/templates.html +906 -91
- nautobot/project-static/docs/development/core/testing.html +894 -90
- nautobot/project-static/docs/development/core/user-preferences.html +894 -90
- nautobot/project-static/docs/development/index.html +894 -90
- nautobot/project-static/docs/development/jobs/index.html +1271 -453
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +894 -90
- nautobot/project-static/docs/index.html +9032 -13
- 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 +902 -91
- nautobot/project-static/docs/overview/design_philosophy.html +896 -92
- nautobot/project-static/docs/overview/index.html +13 -8228
- nautobot/project-static/docs/release-notes/index.html +1131 -94
- nautobot/project-static/docs/release-notes/version-1.0.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.1.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.2.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.3.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.4.html +894 -90
- nautobot/project-static/docs/release-notes/version-1.5.html +895 -91
- nautobot/project-static/docs/release-notes/version-1.6.html +895 -91
- nautobot/project-static/docs/release-notes/version-2.0.html +894 -90
- nautobot/project-static/docs/release-notes/version-2.1.html +894 -90
- nautobot/project-static/docs/release-notes/version-2.2.html +1137 -196
- 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 +335 -260
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +1025 -175
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +894 -90
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +902 -90
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +894 -90
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +946 -155
- nautobot/project-static/docs/user-guide/administration/installation/index.html +903 -95
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +936 -124
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +956 -159
- nautobot/project-static/docs/user-guide/administration/installation/services.html +915 -114
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +910 -101
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +894 -90
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +894 -90
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +894 -90
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +894 -90
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +977 -121
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +894 -90
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +895 -91
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +898 -90
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +897 -93
- 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 +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +925 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +925 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +920 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +925 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +915 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +922 -118
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +923 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +916 -107
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +928 -110
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +938 -120
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +930 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +939 -121
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +930 -112
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +923 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +925 -117
- 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 +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +942 -85
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +945 -88
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +931 -127
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +908 -104
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +938 -90
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +899 -91
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +899 -91
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +903 -98
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +894 -90
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +899 -91
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +894 -90
- 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 +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +894 -90
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +894 -90
- nautobot/project-static/docs/user-guide/index.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1260 -787
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +898 -90
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +897 -93
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +952 -123
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +894 -90
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +894 -90
- 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 +12 -8
- 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.8.dist-info → nautobot-2.3.0.dist-info}/METADATA +21 -19
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/RECORD +684 -564
- 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.8.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
- {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
nautobot/core/tests/test_jobs.py
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
+
from datetime import timedelta
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.core.cache import cache
|
|
6
|
+
from django.core.files.base import ContentFile
|
|
7
|
+
from django.utils import timezone
|
|
4
8
|
import yaml
|
|
5
9
|
|
|
10
|
+
from nautobot.core.jobs.cleanup import CleanupTypes
|
|
6
11
|
from nautobot.core.testing import create_job_result_and_run_job, TransactionTestCase
|
|
7
|
-
from nautobot.dcim.models import DeviceType, Location, LocationType, Manufacturer
|
|
12
|
+
from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer
|
|
8
13
|
from nautobot.extras.choices import JobResultStatusChoices, LogLevelChoices
|
|
9
|
-
from nautobot.extras.
|
|
14
|
+
from nautobot.extras.factory import JobResultFactory, ObjectChangeFactory
|
|
15
|
+
from nautobot.extras.models import (
|
|
16
|
+
Contact,
|
|
17
|
+
ContactAssociation,
|
|
18
|
+
ExportTemplate,
|
|
19
|
+
FileProxy,
|
|
20
|
+
JobLogEntry,
|
|
21
|
+
JobResult,
|
|
22
|
+
ObjectChange,
|
|
23
|
+
Role,
|
|
24
|
+
Status,
|
|
25
|
+
)
|
|
26
|
+
from nautobot.ipam.models import Prefix
|
|
10
27
|
from nautobot.users.models import ObjectPermission
|
|
11
28
|
|
|
12
29
|
|
|
@@ -204,6 +221,76 @@ class ImportObjectsTestCase(TransactionTestCase):
|
|
|
204
221
|
)
|
|
205
222
|
self.assertEqual(4, Status.objects.filter(name__startswith="test_status").count())
|
|
206
223
|
|
|
224
|
+
def test_csv_import_with_utf_8_with_bom_encoding(self):
|
|
225
|
+
"""
|
|
226
|
+
A superuser running the job with a .csv file with utf_8 with bom encoding should successfully create all specified objects.
|
|
227
|
+
Test for bug fix https://github.com/nautobot/nautobot/issues/5812 and https://github.com/nautobot/nautobot/issues/5985
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
status = Status.objects.get(name="Active").pk
|
|
231
|
+
content = f"prefix,status\n192.168.1.1/32,{status}"
|
|
232
|
+
content = content.encode("utf-8-sig")
|
|
233
|
+
filename = "test.csv"
|
|
234
|
+
csv_file = FileProxy.objects.create(name=filename, file=ContentFile(content, name=filename))
|
|
235
|
+
job_result = create_job_result_and_run_job(
|
|
236
|
+
"nautobot.core.jobs",
|
|
237
|
+
"ImportObjects",
|
|
238
|
+
content_type=ContentType.objects.get_for_model(Prefix).pk,
|
|
239
|
+
csv_file=csv_file.id,
|
|
240
|
+
)
|
|
241
|
+
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
|
|
242
|
+
self.assertFalse(
|
|
243
|
+
JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_WARNING).exists()
|
|
244
|
+
)
|
|
245
|
+
self.assertFalse(
|
|
246
|
+
JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR).exists()
|
|
247
|
+
)
|
|
248
|
+
self.assertEqual(
|
|
249
|
+
1, Prefix.objects.filter(status=Status.objects.get(name="Active"), prefix="192.168.1.1/32").count()
|
|
250
|
+
)
|
|
251
|
+
mfr = Manufacturer.objects.create(name="Test Cisco Manufacturer")
|
|
252
|
+
device_type = DeviceType.objects.create(
|
|
253
|
+
manufacturer=mfr,
|
|
254
|
+
model="Cisco CSR1000v",
|
|
255
|
+
u_height=0,
|
|
256
|
+
)
|
|
257
|
+
location_type = LocationType.objects.create(name="Test Location Type")
|
|
258
|
+
location_type.content_types.set([ContentType.objects.get_for_model(Device)])
|
|
259
|
+
location = Location.objects.create(
|
|
260
|
+
name="Device Location",
|
|
261
|
+
location_type=location_type,
|
|
262
|
+
status=Status.objects.get_for_model(Location).first(),
|
|
263
|
+
)
|
|
264
|
+
role = Role.objects.create(name="Device Status")
|
|
265
|
+
role.content_types.set([ContentType.objects.get_for_model(Device)])
|
|
266
|
+
content = "\n".join(
|
|
267
|
+
[
|
|
268
|
+
"serial,asset_tag,device_type,location,status,name,role",
|
|
269
|
+
f"1021C4,CA211,{device_type.pk},{location.pk},{status},Test-AC-01,{role}",
|
|
270
|
+
f"1021C5,CA212,{device_type.pk},{location.pk},{status},Test-AC-02,{role}",
|
|
271
|
+
]
|
|
272
|
+
)
|
|
273
|
+
content = content.encode("utf-8-sig")
|
|
274
|
+
filename = "test.csv"
|
|
275
|
+
csv_file = FileProxy.objects.create(name=filename, file=ContentFile(content, name=filename))
|
|
276
|
+
job_result = create_job_result_and_run_job(
|
|
277
|
+
"nautobot.core.jobs",
|
|
278
|
+
"ImportObjects",
|
|
279
|
+
content_type=ContentType.objects.get_for_model(Device).pk,
|
|
280
|
+
csv_file=csv_file.id,
|
|
281
|
+
)
|
|
282
|
+
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
|
|
283
|
+
self.assertFalse(
|
|
284
|
+
JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_WARNING).exists()
|
|
285
|
+
)
|
|
286
|
+
self.assertFalse(
|
|
287
|
+
JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR).exists()
|
|
288
|
+
)
|
|
289
|
+
device_1 = Device.objects.get(name="Test-AC-01")
|
|
290
|
+
device_2 = Device.objects.get(name="Test-AC-02")
|
|
291
|
+
self.assertEqual(device_1.serial, "1021C4")
|
|
292
|
+
self.assertEqual(device_2.serial, "1021C5")
|
|
293
|
+
|
|
207
294
|
def test_csv_import_bad_row(self):
|
|
208
295
|
"""A row of incorrect data should fail validation for that object but import all others successfully if `roll_back_if_error` is False."""
|
|
209
296
|
csv_data = self.csv_data.split("\n")
|
|
@@ -333,3 +420,118 @@ class ImportObjectsTestCase(TransactionTestCase):
|
|
|
333
420
|
)
|
|
334
421
|
|
|
335
422
|
self.assertEqual(associations_job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class LogsCleanupTestCase(TransactionTestCase):
|
|
426
|
+
"""
|
|
427
|
+
Test the LogsCleanup system job.
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
databases = ("default", "job_logs")
|
|
431
|
+
|
|
432
|
+
def setUp(self):
|
|
433
|
+
super().setUp()
|
|
434
|
+
# Recall that TransactionTestCase truncates the DB after each test case...
|
|
435
|
+
cache.delete("nautobot.extras.utils.change_logged_models_queryset")
|
|
436
|
+
if ObjectChange.objects.count() < 2:
|
|
437
|
+
ObjectChangeFactory.create_batch(40)
|
|
438
|
+
if JobResult.objects.count() < 2:
|
|
439
|
+
JobResultFactory.create_batch(20)
|
|
440
|
+
|
|
441
|
+
def test_cleanup_without_permission(self):
|
|
442
|
+
"""Job should enforce user permissions on the content-types being deleted."""
|
|
443
|
+
job_result_count = JobResult.objects.count()
|
|
444
|
+
job_log_entry_count = JobLogEntry.objects.count()
|
|
445
|
+
object_change_count = ObjectChange.objects.count()
|
|
446
|
+
|
|
447
|
+
job_result = create_job_result_and_run_job(
|
|
448
|
+
"nautobot.core.jobs.cleanup",
|
|
449
|
+
"LogsCleanup",
|
|
450
|
+
username=self.user.username,
|
|
451
|
+
cleanup_types=[CleanupTypes.JOB_RESULT],
|
|
452
|
+
max_age=0,
|
|
453
|
+
)
|
|
454
|
+
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
|
|
455
|
+
log_error = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
456
|
+
self.assertEqual(log_error.message, f'User "{self.user}" does not have permission to delete JobResult records')
|
|
457
|
+
self.assertEqual(JobResult.objects.count(), job_result_count + 1)
|
|
458
|
+
self.assertGreater(JobLogEntry.objects.count(), job_log_entry_count)
|
|
459
|
+
self.assertEqual(ObjectChange.objects.count(), object_change_count)
|
|
460
|
+
|
|
461
|
+
job_result = create_job_result_and_run_job(
|
|
462
|
+
"nautobot.core.jobs.cleanup",
|
|
463
|
+
"LogsCleanup",
|
|
464
|
+
username=self.user.username,
|
|
465
|
+
cleanup_types=[CleanupTypes.OBJECT_CHANGE],
|
|
466
|
+
max_age=0,
|
|
467
|
+
)
|
|
468
|
+
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_FAILURE)
|
|
469
|
+
log_error = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
470
|
+
self.assertEqual(
|
|
471
|
+
log_error.message, f'User "{self.user}" does not have permission to delete ObjectChange records'
|
|
472
|
+
)
|
|
473
|
+
self.assertEqual(JobResult.objects.count(), job_result_count + 2)
|
|
474
|
+
self.assertGreater(JobLogEntry.objects.count(), job_log_entry_count)
|
|
475
|
+
self.assertEqual(ObjectChange.objects.count(), object_change_count)
|
|
476
|
+
|
|
477
|
+
def test_cleanup_with_constrained_permission(self):
|
|
478
|
+
"""Job should only allow the user to cleanup records they have permission to delete."""
|
|
479
|
+
job_result_1 = JobResult.objects.last()
|
|
480
|
+
job_result_2 = JobResult.objects.first()
|
|
481
|
+
self.assertNotEqual(job_result_1.pk, job_result_2.pk)
|
|
482
|
+
object_change_1 = ObjectChange.objects.first()
|
|
483
|
+
object_change_2 = ObjectChange.objects.last()
|
|
484
|
+
self.assertNotEqual(object_change_1.pk, object_change_2.pk)
|
|
485
|
+
obj_perm = ObjectPermission(
|
|
486
|
+
name="Test permission",
|
|
487
|
+
constraints={"pk__in": [str(job_result_1.pk), str(object_change_1.pk)]},
|
|
488
|
+
actions=["delete"],
|
|
489
|
+
)
|
|
490
|
+
obj_perm.save()
|
|
491
|
+
obj_perm.users.add(self.user)
|
|
492
|
+
obj_perm.object_types.add(ContentType.objects.get_for_model(JobResult))
|
|
493
|
+
obj_perm.object_types.add(ContentType.objects.get_for_model(ObjectChange))
|
|
494
|
+
job_result = create_job_result_and_run_job(
|
|
495
|
+
"nautobot.core.jobs.cleanup",
|
|
496
|
+
"LogsCleanup",
|
|
497
|
+
username=self.user.username,
|
|
498
|
+
cleanup_types=[CleanupTypes.JOB_RESULT, CleanupTypes.OBJECT_CHANGE],
|
|
499
|
+
max_age=0,
|
|
500
|
+
)
|
|
501
|
+
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
|
|
502
|
+
self.assertEqual(job_result.result["extras.JobResult"], 1)
|
|
503
|
+
self.assertEqual(job_result.result["extras.ObjectChange"], 1)
|
|
504
|
+
with self.assertRaises(JobResult.DoesNotExist):
|
|
505
|
+
JobResult.objects.get(pk=job_result_1.pk)
|
|
506
|
+
JobResult.objects.get(pk=job_result_2.pk)
|
|
507
|
+
with self.assertRaises(ObjectChange.DoesNotExist):
|
|
508
|
+
ObjectChange.objects.get(pk=object_change_1.pk)
|
|
509
|
+
ObjectChange.objects.get(pk=object_change_2.pk)
|
|
510
|
+
|
|
511
|
+
def test_cleanup_job_results(self):
|
|
512
|
+
"""With unconstrained permissions, all JobResults before the cutoff should be deleted."""
|
|
513
|
+
cutoff = timezone.now() - timedelta(days=60)
|
|
514
|
+
create_job_result_and_run_job(
|
|
515
|
+
"nautobot.core.jobs.cleanup",
|
|
516
|
+
"LogsCleanup",
|
|
517
|
+
cleanup_types=[CleanupTypes.JOB_RESULT],
|
|
518
|
+
max_age=60,
|
|
519
|
+
)
|
|
520
|
+
self.assertFalse(JobResult.objects.filter(date_done__lt=cutoff).exists())
|
|
521
|
+
self.assertTrue(JobResult.objects.filter(date_done__gte=cutoff).exists())
|
|
522
|
+
self.assertTrue(ObjectChange.objects.filter(time__lt=cutoff).exists())
|
|
523
|
+
self.assertTrue(ObjectChange.objects.filter(time__gte=cutoff).exists())
|
|
524
|
+
|
|
525
|
+
def test_cleanup_object_changes(self):
|
|
526
|
+
"""With unconstrained permissions, all ObjectChanges before the cutoff should be deleted."""
|
|
527
|
+
cutoff = timezone.now() - timedelta(days=60)
|
|
528
|
+
create_job_result_and_run_job(
|
|
529
|
+
"nautobot.core.jobs.cleanup",
|
|
530
|
+
"LogsCleanup",
|
|
531
|
+
cleanup_types=[CleanupTypes.OBJECT_CHANGE],
|
|
532
|
+
max_age=60,
|
|
533
|
+
)
|
|
534
|
+
self.assertTrue(JobResult.objects.filter(date_done__lt=cutoff).exists())
|
|
535
|
+
self.assertTrue(JobResult.objects.filter(date_done__gte=cutoff).exists())
|
|
536
|
+
self.assertFalse(ObjectChange.objects.filter(time__lt=cutoff).exists())
|
|
537
|
+
self.assertTrue(ObjectChange.objects.filter(time__gte=cutoff).exists())
|
|
@@ -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(
|
|
@@ -20,15 +20,15 @@ class NautobotTemplateTagsNetutilsTest(TestCase):
|
|
|
20
20
|
i = 1
|
|
21
21
|
for param_name, param in signature.parameters.items():
|
|
22
22
|
template_string += f" {param_name}="
|
|
23
|
-
if param.annotation
|
|
23
|
+
if param.annotation is str:
|
|
24
24
|
template_string += f'"{i}"'
|
|
25
|
-
elif param.annotation
|
|
25
|
+
elif param.annotation is bool:
|
|
26
26
|
template_string += "True"
|
|
27
27
|
elif param.annotation in (int, float):
|
|
28
28
|
template_string += str(i)
|
|
29
29
|
elif param.annotation in (list, tuple):
|
|
30
30
|
template_string += "[]"
|
|
31
|
-
elif param.annotation
|
|
31
|
+
elif param.annotation is dict:
|
|
32
32
|
template_string += "{}"
|
|
33
33
|
else:
|
|
34
34
|
template_string += "None"
|
|
@@ -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
|
|