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
|
@@ -45,7 +45,7 @@ def render_form(form, excluded_fields=None):
|
|
|
45
45
|
https://github.com/nautobot/nautobot/issues/4503
|
|
46
46
|
"""
|
|
47
47
|
if excluded_fields is None:
|
|
48
|
-
excluded_fields = ["tags", "object_note"]
|
|
48
|
+
excluded_fields = ["tags", "dynamic_groups", "object_note"]
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
"form": form,
|
|
@@ -2,10 +2,14 @@ import datetime
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
|
+
from urllib.parse import parse_qs
|
|
5
6
|
|
|
6
7
|
from django import template
|
|
7
8
|
from django.conf import settings
|
|
9
|
+
from django.contrib import messages
|
|
10
|
+
from django.contrib.auth.models import AnonymousUser
|
|
8
11
|
from django.contrib.staticfiles.finders import find
|
|
12
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
9
13
|
from django.templatetags.static import static, StaticNode
|
|
10
14
|
from django.urls import NoReverseMatch, reverse
|
|
11
15
|
from django.utils.html import format_html, format_html_join
|
|
@@ -192,7 +196,7 @@ def render_markdown(value):
|
|
|
192
196
|
|
|
193
197
|
@library.filter()
|
|
194
198
|
@register.filter()
|
|
195
|
-
def render_json(value, syntax_highlight=True):
|
|
199
|
+
def render_json(value, syntax_highlight=True, pretty_print=False):
|
|
196
200
|
"""
|
|
197
201
|
Render a dictionary as formatted JSON.
|
|
198
202
|
|
|
@@ -201,7 +205,11 @@ def render_json(value, syntax_highlight=True):
|
|
|
201
205
|
"""
|
|
202
206
|
rendered_json = json.dumps(value, indent=4, sort_keys=True, ensure_ascii=False)
|
|
203
207
|
if syntax_highlight:
|
|
204
|
-
|
|
208
|
+
html_string = '<code class="language-json">{}</code>'
|
|
209
|
+
if pretty_print:
|
|
210
|
+
html_string = "<pre>" + html_string + "</pre>"
|
|
211
|
+
return format_html(html_string, rendered_json)
|
|
212
|
+
|
|
205
213
|
return rendered_json
|
|
206
214
|
|
|
207
215
|
|
|
@@ -581,6 +589,33 @@ def slugify(value):
|
|
|
581
589
|
return django_slugify(value)
|
|
582
590
|
|
|
583
591
|
|
|
592
|
+
@library.filter()
|
|
593
|
+
@register.filter()
|
|
594
|
+
def render_uptime(seconds):
|
|
595
|
+
"""Format a value in seconds to a human readable value.
|
|
596
|
+
|
|
597
|
+
Example:
|
|
598
|
+
>>> render_uptime(1024768)
|
|
599
|
+
"11 days 20 hours 39 minutes"
|
|
600
|
+
"""
|
|
601
|
+
try:
|
|
602
|
+
seconds = int(seconds)
|
|
603
|
+
except ValueError:
|
|
604
|
+
return placeholder(seconds)
|
|
605
|
+
delta = datetime.timedelta(seconds=seconds)
|
|
606
|
+
uptime_hours = delta.seconds // 3600
|
|
607
|
+
uptime_minutes = delta.seconds // 60 % 60
|
|
608
|
+
return format_html(
|
|
609
|
+
"{} {} {} {} {} {}",
|
|
610
|
+
delta.days,
|
|
611
|
+
"days" if delta.days != 1 else "day",
|
|
612
|
+
uptime_hours,
|
|
613
|
+
"hours" if uptime_hours != 1 else "hour",
|
|
614
|
+
uptime_minutes,
|
|
615
|
+
"minutes" if uptime_minutes != 1 else "minute",
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
|
|
584
619
|
#
|
|
585
620
|
# Tags
|
|
586
621
|
#
|
|
@@ -609,6 +644,23 @@ def querystring(request, **kwargs):
|
|
|
609
644
|
return ""
|
|
610
645
|
|
|
611
646
|
|
|
647
|
+
@register.simple_tag()
|
|
648
|
+
def table_config_button(table, table_name=None, extra_classes=""):
|
|
649
|
+
if table_name is None:
|
|
650
|
+
table_name = table.__class__.__name__
|
|
651
|
+
html_template = (
|
|
652
|
+
'<button type="button" class="btn btn-default {}'
|
|
653
|
+
'" data-toggle="modal" data-target="#{}_config" title="Configure table">'
|
|
654
|
+
'<i class="mdi mdi-cog"></i> Configure</button>'
|
|
655
|
+
)
|
|
656
|
+
return format_html(html_template, extra_classes, table_name)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@register.simple_tag()
|
|
660
|
+
def table_config_button_small(table, table_name=None):
|
|
661
|
+
return table_config_button(table, table_name, "btn-xs")
|
|
662
|
+
|
|
663
|
+
|
|
612
664
|
@register.inclusion_tag("utilities/templatetags/utilization_graph.html")
|
|
613
665
|
def utilization_graph(utilization_data, warning_threshold=75, danger_threshold=90):
|
|
614
666
|
"""Wrapper for a horizontal bar graph indicating a percentage of utilization from a tuple of data.
|
|
@@ -714,6 +766,121 @@ def filter_form_modal(
|
|
|
714
766
|
}
|
|
715
767
|
|
|
716
768
|
|
|
769
|
+
@register.inclusion_tag("utilities/templatetags/saved_view_modal.html")
|
|
770
|
+
def saved_view_modal(
|
|
771
|
+
params,
|
|
772
|
+
view,
|
|
773
|
+
model,
|
|
774
|
+
request,
|
|
775
|
+
):
|
|
776
|
+
from nautobot.extras.forms import SavedViewModalForm
|
|
777
|
+
from nautobot.extras.models import SavedView
|
|
778
|
+
|
|
779
|
+
param_dict = {}
|
|
780
|
+
filters_applied = parse_qs(params)
|
|
781
|
+
|
|
782
|
+
sort_order = []
|
|
783
|
+
per_page = None
|
|
784
|
+
table_changes_pending = False
|
|
785
|
+
all_filters_removed = False
|
|
786
|
+
current_saved_view = None
|
|
787
|
+
current_saved_view_pk = None
|
|
788
|
+
non_filter_params = [
|
|
789
|
+
"all_filters_removed",
|
|
790
|
+
"page",
|
|
791
|
+
"per_page",
|
|
792
|
+
"sort",
|
|
793
|
+
"saved_view",
|
|
794
|
+
"table_changes_pending",
|
|
795
|
+
"clear_view",
|
|
796
|
+
]
|
|
797
|
+
|
|
798
|
+
table_name = lookup.get_table_for_model(model).__name__
|
|
799
|
+
for param in non_filter_params:
|
|
800
|
+
if param == "saved_view":
|
|
801
|
+
current_saved_view_pk = filters_applied.pop(param, None)
|
|
802
|
+
if current_saved_view_pk:
|
|
803
|
+
current_saved_view_pk = current_saved_view_pk[0]
|
|
804
|
+
try:
|
|
805
|
+
# We are not using .restrict(request.user, "view") here
|
|
806
|
+
# User should be able to see any saved view that he has the list view access to.
|
|
807
|
+
current_saved_view = SavedView.objects.get(pk=current_saved_view_pk)
|
|
808
|
+
except ObjectDoesNotExist:
|
|
809
|
+
messages.error(request, f"Saved view {current_saved_view_pk} not found")
|
|
810
|
+
|
|
811
|
+
elif param == "table_changes_pending":
|
|
812
|
+
table_changes_pending = filters_applied.pop(param, False)
|
|
813
|
+
elif param == "all_filters_removed":
|
|
814
|
+
all_filters_removed = filters_applied.pop(param, False)
|
|
815
|
+
elif param == "per_page":
|
|
816
|
+
per_page = filters_applied.pop(param, None)
|
|
817
|
+
elif param == "sort":
|
|
818
|
+
sort_order = filters_applied.pop(param, [])
|
|
819
|
+
elif param == "clear_view":
|
|
820
|
+
filters_applied.pop(param, False)
|
|
821
|
+
|
|
822
|
+
if filters_applied:
|
|
823
|
+
param_dict["filter_params"] = filters_applied
|
|
824
|
+
else:
|
|
825
|
+
if (current_saved_view is not None and all_filters_removed) or (current_saved_view is None):
|
|
826
|
+
# user removed all the filters in a saved view
|
|
827
|
+
param_dict["filter_params"] = {}
|
|
828
|
+
elif current_saved_view is not None:
|
|
829
|
+
# user did not make any changes to the saved view filter params
|
|
830
|
+
param_dict["filter_params"] = current_saved_view.config.get("filter_params", {})
|
|
831
|
+
|
|
832
|
+
if current_saved_view is not None and not table_changes_pending:
|
|
833
|
+
# user did not make any changes to the saved view table config
|
|
834
|
+
view_table_config = current_saved_view.config.get("table_config", {}).get(f"{table_name}", None)
|
|
835
|
+
if view_table_config is not None:
|
|
836
|
+
param_dict["table_config"] = view_table_config.get("columns", [])
|
|
837
|
+
else:
|
|
838
|
+
# display default user display
|
|
839
|
+
if request.user is not None and not isinstance(request.user, AnonymousUser):
|
|
840
|
+
param_dict["table_config"] = request.user.get_config(f"tables.{table_name}.columns")
|
|
841
|
+
# If both are not available, do not display table_config
|
|
842
|
+
|
|
843
|
+
if per_page:
|
|
844
|
+
# user made changes to saved view pagination count
|
|
845
|
+
param_dict["per_page"] = per_page
|
|
846
|
+
elif current_saved_view is not None and not per_page:
|
|
847
|
+
# no changes made, display current saved view pagination count
|
|
848
|
+
param_dict["per_page"] = current_saved_view.config.get(
|
|
849
|
+
"pagination_count", config.get_settings_or_config("PAGINATE_COUNT")
|
|
850
|
+
)
|
|
851
|
+
else:
|
|
852
|
+
# display default pagination count
|
|
853
|
+
param_dict["per_page"] = config.get_settings_or_config("PAGINATE_COUNT")
|
|
854
|
+
|
|
855
|
+
if sort_order:
|
|
856
|
+
# user made changes to saved view sort order
|
|
857
|
+
param_dict["sort_order"] = sort_order
|
|
858
|
+
elif current_saved_view is not None and not sort_order:
|
|
859
|
+
# no changes made, display current saved view sort order
|
|
860
|
+
param_dict["sort_order"] = current_saved_view.config.get("sort_order", [])
|
|
861
|
+
else:
|
|
862
|
+
# no sorting applied
|
|
863
|
+
param_dict["sort_order"] = []
|
|
864
|
+
|
|
865
|
+
param_dict = json.dumps(param_dict, indent=4, sort_keys=True, ensure_ascii=False)
|
|
866
|
+
return {
|
|
867
|
+
"form": SavedViewModalForm(),
|
|
868
|
+
"params": params,
|
|
869
|
+
"param_dict": param_dict,
|
|
870
|
+
"view": view,
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
@register.inclusion_tag("utilities/templatetags/dynamic_group_assignment_modal.html")
|
|
875
|
+
def dynamic_group_assignment_modal(request, content_type):
|
|
876
|
+
from nautobot.extras.forms import DynamicGroupBulkAssignForm
|
|
877
|
+
|
|
878
|
+
return {
|
|
879
|
+
"request": request,
|
|
880
|
+
"form": DynamicGroupBulkAssignForm(model=content_type.model_class()),
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
|
|
717
884
|
@register.inclusion_tag("utilities/templatetags/modal_form_as_dialog.html")
|
|
718
885
|
def modal_form_as_dialog(form, editing=False, form_name=None, obj=None, obj_type=None):
|
|
719
886
|
"""Generate a form in a modal view.
|
|
@@ -806,7 +973,7 @@ def hyperlinked_object_with_color(obj):
|
|
|
806
973
|
if obj:
|
|
807
974
|
content = f'<span class="label" style="color: {fgcolor(obj.color)}; background-color: #{obj.color}">{hyperlinked_object(obj)}</span>'
|
|
808
975
|
return format_html(content)
|
|
809
|
-
return
|
|
976
|
+
return HTML_NONE
|
|
810
977
|
|
|
811
978
|
|
|
812
979
|
@register.filter()
|
|
@@ -870,12 +1037,15 @@ def _build_hyperlink(value, field="", target="", rel=""):
|
|
|
870
1037
|
attributes = {}
|
|
871
1038
|
display = getattr(value, field) if hasattr(value, field) else str(value)
|
|
872
1039
|
if hasattr(value, "get_absolute_url"):
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1040
|
+
try:
|
|
1041
|
+
attributes["href"] = value.get_absolute_url()
|
|
1042
|
+
if hasattr(value, "description") and value.description:
|
|
1043
|
+
attributes["title"] = value.description
|
|
1044
|
+
if target:
|
|
1045
|
+
attributes["target"] = target
|
|
1046
|
+
if rel:
|
|
1047
|
+
attributes["rel"] = rel
|
|
1048
|
+
return format_html("<a {}>{}</a>", format_html_join(" ", '{}="{}"', attributes.items()), display)
|
|
1049
|
+
except AttributeError:
|
|
1050
|
+
pass
|
|
881
1051
|
return format_html("{}", display)
|
nautobot/core/testing/api.py
CHANGED
|
@@ -740,7 +740,11 @@ class APIViewTestCases:
|
|
|
740
740
|
|
|
741
741
|
initial_count = self._get_queryset().count()
|
|
742
742
|
response = self.client.post(self._get_list_url(), self.create_data, format="json", **self.header)
|
|
743
|
-
self.assertHttpStatus(
|
|
743
|
+
self.assertHttpStatus(
|
|
744
|
+
response,
|
|
745
|
+
status.HTTP_201_CREATED,
|
|
746
|
+
msg=f"create_data: {self.create_data}\nexisting records: {list(self._get_queryset())}",
|
|
747
|
+
)
|
|
744
748
|
self.assertEqual(len(response.data), len(self.create_data))
|
|
745
749
|
self.assertEqual(self._get_queryset().count(), initial_count + len(self.create_data))
|
|
746
750
|
for i, obj in enumerate(response.data):
|
|
@@ -841,7 +845,6 @@ class APIViewTestCases:
|
|
|
841
845
|
# This may change (hah) at some point -- see https://github.com/nautobot/nautobot/issues/3321
|
|
842
846
|
if hasattr(self.model, "to_objectchange"):
|
|
843
847
|
objectchanges = lookup.get_changes_for_model(instance)
|
|
844
|
-
self.assertEqual(len(objectchanges), 1)
|
|
845
848
|
self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_UPDATE)
|
|
846
849
|
objectchanges.delete()
|
|
847
850
|
|
|
@@ -858,7 +861,6 @@ class APIViewTestCases:
|
|
|
858
861
|
# Verify ObjectChange creation
|
|
859
862
|
if hasattr(self.model, "to_objectchange"):
|
|
860
863
|
objectchanges = lookup.get_changes_for_model(instance)
|
|
861
|
-
self.assertEqual(len(objectchanges), 1)
|
|
862
864
|
self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_UPDATE)
|
|
863
865
|
|
|
864
866
|
def test_get_put_round_trip(self):
|
|
@@ -1030,7 +1032,6 @@ class APIViewTestCases:
|
|
|
1030
1032
|
# Verify ObjectChange creation
|
|
1031
1033
|
if hasattr(self.model, "to_objectchange"):
|
|
1032
1034
|
objectchanges = lookup.get_changes_for_model(instance)
|
|
1033
|
-
self.assertEqual(len(objectchanges), 1)
|
|
1034
1035
|
self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_DELETE)
|
|
1035
1036
|
|
|
1036
1037
|
def test_bulk_delete_objects(self):
|
nautobot/core/testing/filters.py
CHANGED
|
@@ -3,10 +3,12 @@ import string
|
|
|
3
3
|
|
|
4
4
|
from django.contrib.contenttypes.models import ContentType
|
|
5
5
|
from django.db.models import Count, Q
|
|
6
|
+
from django.db.models.fields import CharField, TextField
|
|
6
7
|
from django.db.models.fields.related import ManyToManyField
|
|
7
8
|
from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
|
|
8
9
|
from django.test import tag
|
|
9
10
|
|
|
11
|
+
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
10
12
|
from nautobot.core.filters import (
|
|
11
13
|
ContentTypeChoiceFilter,
|
|
12
14
|
ContentTypeFilter,
|
|
@@ -16,6 +18,7 @@ from nautobot.core.filters import (
|
|
|
16
18
|
)
|
|
17
19
|
from nautobot.core.models.generics import PrimaryModel
|
|
18
20
|
from nautobot.core.testing import views
|
|
21
|
+
from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Tag, Team
|
|
19
22
|
from nautobot.tenancy import models
|
|
20
23
|
|
|
21
24
|
|
|
@@ -119,6 +122,42 @@ class FilterTestCases:
|
|
|
119
122
|
)
|
|
120
123
|
This expects a field named `devices` on the model and a filter named `devices` on the filterset.
|
|
121
124
|
"""
|
|
125
|
+
if getattr(self.queryset.model, "is_contact_associable_model", False):
|
|
126
|
+
for generic_filter_test in (
|
|
127
|
+
["contacts", "associated_contacts__contact__name"],
|
|
128
|
+
["contacts", "associated_contacts__contact__id"],
|
|
129
|
+
["teams", "associated_contacts__team__name"],
|
|
130
|
+
["teams", "associated_contacts__team__id"],
|
|
131
|
+
):
|
|
132
|
+
if generic_filter_test not in self.generic_filter_tests:
|
|
133
|
+
self.generic_filter_tests = (*self.generic_filter_tests, generic_filter_test)
|
|
134
|
+
|
|
135
|
+
# Make sure we have at least 3 contacts and 3 teams in the database
|
|
136
|
+
if Contact.objects.count() < 3:
|
|
137
|
+
Contact.objects.create(name="Generic Filter Test Contact 1")
|
|
138
|
+
Contact.objects.create(name="Generic Filter Test Contact 2")
|
|
139
|
+
Contact.objects.create(name="Generic Filter Test Contact 3")
|
|
140
|
+
|
|
141
|
+
if Team.objects.count() < 3:
|
|
142
|
+
Team.objects.create(name="Generic Filter Test Team 1")
|
|
143
|
+
Team.objects.create(name="Generic Filter Test Team 2")
|
|
144
|
+
Team.objects.create(name="Generic Filter Test Team 3")
|
|
145
|
+
|
|
146
|
+
# Make sure we have some valid contact-associations:
|
|
147
|
+
for contact, team, instance in zip(Contact.objects.all()[:3], Team.objects.all()[:3], self.queryset):
|
|
148
|
+
ContactAssociation.objects.create(
|
|
149
|
+
contact=contact,
|
|
150
|
+
associated_object=instance,
|
|
151
|
+
role=Role.objects.get_for_model(ContactAssociation).first(),
|
|
152
|
+
status=Status.objects.get_for_model(ContactAssociation).first(),
|
|
153
|
+
)
|
|
154
|
+
ContactAssociation.objects.create(
|
|
155
|
+
team=team,
|
|
156
|
+
associated_object=instance,
|
|
157
|
+
role=Role.objects.get_for_model(ContactAssociation).last(),
|
|
158
|
+
status=Status.objects.get_for_model(ContactAssociation).last(),
|
|
159
|
+
)
|
|
160
|
+
|
|
122
161
|
if not self.generic_filter_tests:
|
|
123
162
|
self.skipTest("No generic_filter_tests defined?")
|
|
124
163
|
|
|
@@ -147,11 +186,11 @@ class FilterTestCases:
|
|
|
147
186
|
field_name = filter_object.field_name
|
|
148
187
|
with self.subTest(f"{self.filterset.__name__} RelatedMembershipBooleanFilter {filter_name} (True)"):
|
|
149
188
|
filterset_result = self.filterset({filter_name: True}, self.queryset).qs
|
|
150
|
-
qs_result = self.queryset.filter(**{f"{field_name}__isnull":
|
|
189
|
+
qs_result = self.queryset.filter(**{f"{field_name}__isnull": filter_object.exclude}).distinct()
|
|
151
190
|
self.assertQuerysetEqualAndNotEmpty(filterset_result, qs_result)
|
|
152
191
|
with self.subTest(f"{self.filterset.__name__} RelatedMembershipBooleanFilter {filter_name} (False)"):
|
|
153
192
|
filterset_result = self.filterset({filter_name: False}, self.queryset).qs
|
|
154
|
-
qs_result = self.queryset.
|
|
193
|
+
qs_result = self.queryset.exclude(**{f"{field_name}__isnull": filter_object.exclude}).distinct()
|
|
155
194
|
self.assertQuerysetEqualAndNotEmpty(filterset_result, qs_result)
|
|
156
195
|
|
|
157
196
|
def test_tags_filter(self):
|
|
@@ -164,8 +203,16 @@ class FilterTestCases:
|
|
|
164
203
|
if len(instance.tags.all()) >= 2:
|
|
165
204
|
tags = list(instance.tags.all()[:2])
|
|
166
205
|
break
|
|
206
|
+
|
|
207
|
+
# Otherwise, create some tags and apply to an instance for this test
|
|
167
208
|
else:
|
|
168
|
-
|
|
209
|
+
model_ct = ContentType.objects.get_for_model(self.queryset.model)
|
|
210
|
+
test_tags_filter_a = Tag.objects.get_or_create(name="test tags filter a")[0]
|
|
211
|
+
test_tags_filter_a.content_types.add(model_ct)
|
|
212
|
+
test_tags_filter_b = Tag.objects.get_or_create(name="test tags filter b")[0]
|
|
213
|
+
test_tags_filter_b.content_types.add(model_ct)
|
|
214
|
+
self.queryset.first().tags.add(test_tags_filter_a, test_tags_filter_b)
|
|
215
|
+
tags = [test_tags_filter_a, test_tags_filter_b]
|
|
169
216
|
params = {"tags": [tags[0].name, tags[1].pk]}
|
|
170
217
|
filterset_result = self.filterset(params, self.queryset).qs
|
|
171
218
|
# Tags is an AND filter not an OR filter
|
|
@@ -222,22 +269,23 @@ class FilterTestCases:
|
|
|
222
269
|
"""
|
|
223
270
|
self._assert_valid_filter_predicates(obj, obj_field_name)
|
|
224
271
|
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
272
|
+
# Generic test only supports CharField or TextFields, skip all other types
|
|
273
|
+
obj_field = obj._meta.get_field(obj_field_name)
|
|
274
|
+
if not isinstance(obj_field, (CharField, TextField)):
|
|
275
|
+
self.skipTest("Not a CharField or TextField")
|
|
228
276
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
# Skip the test if the field is not a CharField, as we currently only support generic testing for CharField
|
|
234
|
-
self.skipTest("Not a CharField")
|
|
277
|
+
# Create random lowercase string to use for icontains lookup
|
|
278
|
+
max_length = obj_field.max_length or CHARFIELD_MAX_LENGTH
|
|
279
|
+
randomized_attr_value = "".join(random.choices(string.ascii_lowercase, k=max_length)) # noqa: S311 # pseudo-random generator
|
|
280
|
+
setattr(obj, obj_field_name, randomized_attr_value)
|
|
235
281
|
obj.save()
|
|
282
|
+
|
|
236
283
|
# if lookup_method is iexact use the full updated attr
|
|
237
284
|
if lookup_method == "iexact":
|
|
238
|
-
lookup =
|
|
239
|
-
model_queryset = self.queryset.filter(**{f"{filter_field_name}": lookup})
|
|
285
|
+
lookup = randomized_attr_value.upper()
|
|
286
|
+
model_queryset = self.queryset.filter(**{f"{filter_field_name}__iexact": lookup})
|
|
240
287
|
else:
|
|
288
|
+
lookup = randomized_attr_value[1:].upper()
|
|
241
289
|
model_queryset = self.queryset.filter(**{f"{filter_field_name}__icontains": lookup})
|
|
242
290
|
params = {"q": lookup}
|
|
243
291
|
filterset_result = self.filterset(params, self.queryset)
|
|
@@ -247,6 +295,7 @@ class FilterTestCases:
|
|
|
247
295
|
filterset_result.qs,
|
|
248
296
|
model_queryset,
|
|
249
297
|
ordered=False,
|
|
298
|
+
msg=lookup,
|
|
250
299
|
)
|
|
251
300
|
|
|
252
301
|
def _get_relevant_filterset_queryset(self, queryset, *filter_params):
|
nautobot/core/testing/mixins.py
CHANGED
|
@@ -6,8 +6,10 @@ from django.contrib.auth import get_user_model
|
|
|
6
6
|
from django.contrib.contenttypes.models import ContentType
|
|
7
7
|
from django.core.cache import cache
|
|
8
8
|
from django.core.exceptions import FieldDoesNotExist
|
|
9
|
+
from django.db import connections, DEFAULT_DB_ALIAS
|
|
9
10
|
from django.db.models import JSONField, ManyToManyField, ManyToManyRel
|
|
10
11
|
from django.forms.models import model_to_dict
|
|
12
|
+
from django.test.utils import CaptureQueriesContext
|
|
11
13
|
from netaddr import IPNetwork
|
|
12
14
|
from rest_framework.test import APIClient, APIRequestFactory
|
|
13
15
|
|
|
@@ -44,6 +46,7 @@ class NautobotTestCaseMixin:
|
|
|
44
46
|
|
|
45
47
|
user_permissions = ()
|
|
46
48
|
client_class = NautobotTestClient
|
|
49
|
+
maxDiff = None
|
|
47
50
|
|
|
48
51
|
def setUpNautobot(self, client=True, populate_status=False):
|
|
49
52
|
"""Setup shared testuser, statuses and client."""
|
|
@@ -231,6 +234,49 @@ class NautobotTestCaseMixin:
|
|
|
231
234
|
|
|
232
235
|
return self.assertQuerysetEqual(qs, values, *args, **kwargs)
|
|
233
236
|
|
|
237
|
+
class _AssertApproximateNumQueriesContext(CaptureQueriesContext):
|
|
238
|
+
"""Implementation class underlying the assertApproximateNumQueries decorator/context manager."""
|
|
239
|
+
|
|
240
|
+
def __init__(self, test_case, minimum, maximum, connection):
|
|
241
|
+
self.test_case = test_case
|
|
242
|
+
self.minimum = minimum
|
|
243
|
+
self.maximum = maximum
|
|
244
|
+
super().__init__(connection)
|
|
245
|
+
|
|
246
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
247
|
+
super().__exit__(exc_type, exc_value, traceback)
|
|
248
|
+
if exc_type is not None:
|
|
249
|
+
return
|
|
250
|
+
num_queries = len(self)
|
|
251
|
+
captured_queries_string = "\n".join(
|
|
252
|
+
f"{i}. {query['sql']}" for i, query in enumerate(self.captured_queries, start=1)
|
|
253
|
+
)
|
|
254
|
+
self.test_case.assertGreaterEqual(
|
|
255
|
+
num_queries,
|
|
256
|
+
self.minimum,
|
|
257
|
+
f"{num_queries} queries executed, but expected at least {self.minimum}.\n"
|
|
258
|
+
f"Captured queries were:\n{captured_queries_string}",
|
|
259
|
+
)
|
|
260
|
+
self.test_case.assertLessEqual(
|
|
261
|
+
num_queries,
|
|
262
|
+
self.maximum,
|
|
263
|
+
f"{num_queries} queries executed, but expected no more than {self.maximum}.\n"
|
|
264
|
+
f"Captured queries were:\n{captured_queries_string}",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def assertApproximateNumQueries(self, minimum, maximum, func=None, *args, using=DEFAULT_DB_ALIAS, **kwargs):
|
|
268
|
+
"""Like assertNumQueries, but fuzzier. Assert that the number of queries falls within an acceptable range."""
|
|
269
|
+
conn = connections[using]
|
|
270
|
+
|
|
271
|
+
context = self._AssertApproximateNumQueriesContext(self, minimum, maximum, conn)
|
|
272
|
+
if func is None:
|
|
273
|
+
return context
|
|
274
|
+
|
|
275
|
+
with context:
|
|
276
|
+
func(*args, **kwargs)
|
|
277
|
+
|
|
278
|
+
return None
|
|
279
|
+
|
|
234
280
|
#
|
|
235
281
|
# Convenience methods
|
|
236
282
|
#
|
nautobot/core/testing/models.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
from django.db.models import QuerySet
|
|
1
2
|
from django.test import tag, TestCase
|
|
2
3
|
|
|
3
4
|
from nautobot.core.templatetags.helpers import get_docs_url
|
|
4
5
|
from nautobot.core.testing.mixins import NautobotTestCaseMixin
|
|
6
|
+
from nautobot.extras.models import DynamicGroup, StaticGroupAssociation
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
@tag("unit")
|
|
@@ -41,3 +43,23 @@ class ModelTestCases:
|
|
|
41
43
|
def test_get_docs_url(self):
|
|
42
44
|
"""Check that `get_docs_url()` returns a valid static file path for this model."""
|
|
43
45
|
self.assertIsNotNone(get_docs_url(self.model))
|
|
46
|
+
|
|
47
|
+
def test_dynamic_group_api(self):
|
|
48
|
+
"""For dynamic-group capable models, check that they work as intended."""
|
|
49
|
+
if not getattr(self.model, "is_dynamic_group_associable_model", False):
|
|
50
|
+
self.skipTest("Not a dynamic group associable model.")
|
|
51
|
+
|
|
52
|
+
self.assertTrue(hasattr(self.model, "dynamic_groups"))
|
|
53
|
+
self.assertIsInstance(self.model.objects.first().dynamic_groups, QuerySet)
|
|
54
|
+
self.assertEqual(self.model.objects.first().dynamic_groups.model, DynamicGroup)
|
|
55
|
+
|
|
56
|
+
if DynamicGroup.objects.get_for_model(self.model).exists():
|
|
57
|
+
dg = DynamicGroup.objects.get_for_model(self.model).first()
|
|
58
|
+
self.assertEqual(dg.members.model, self.model)
|
|
59
|
+
|
|
60
|
+
# Models using DynamicGroupMixin w/o DynamicGroupsModelMixin will not have static_group_association_set
|
|
61
|
+
if hasattr(self.model, "static_group_association_set"):
|
|
62
|
+
self.assertIsInstance(self.model.objects.first().static_group_association_set.all(), QuerySet)
|
|
63
|
+
self.assertEqual(
|
|
64
|
+
self.model.objects.first().static_group_association_set.all().model, StaticGroupAssociation
|
|
65
|
+
)
|
nautobot/core/testing/schema.py
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
from io import StringIO
|
|
2
|
-
|
|
3
1
|
from django.conf import settings
|
|
4
|
-
from django.core.management import call_command
|
|
5
2
|
from django.test import tag
|
|
3
|
+
from drf_spectacular.settings import spectacular_settings
|
|
6
4
|
from rest_framework.settings import api_settings
|
|
7
|
-
import yaml
|
|
8
5
|
|
|
9
6
|
from nautobot.core.testing import views
|
|
10
7
|
|
|
@@ -19,11 +16,10 @@ class OpenAPISchemaTestCases:
|
|
|
19
16
|
# We could load the schema from the /api/swagger.yaml endpoint in setUp(self) via self.client,
|
|
20
17
|
# but it's fairly expensive to do so. Better to do so only once per class.
|
|
21
18
|
cls.schemas = {}
|
|
19
|
+
generator_class = spectacular_settings.DEFAULT_GENERATOR_CLASS
|
|
22
20
|
for api_version in api_settings.ALLOWED_VERSIONS:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
call_command("spectacular", "--api-version", api_version, stdout=out, stderr=err)
|
|
26
|
-
cls.schemas[api_version] = yaml.safe_load(out.getvalue())
|
|
21
|
+
generator = generator_class(api_version=api_version)
|
|
22
|
+
cls.schemas[api_version] = generator.get_schema(request=None, public=True)
|
|
27
23
|
|
|
28
24
|
def get_component_schema(self, component_name, api_version=None):
|
|
29
25
|
"""Helper method to pull a specific component schema from the larger OpenAPI schema already loaded."""
|