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/filters.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from collections import OrderedDict
|
|
2
1
|
from copy import deepcopy
|
|
3
2
|
import logging
|
|
4
3
|
import uuid
|
|
@@ -7,9 +6,11 @@ from django import forms as django_forms
|
|
|
7
6
|
from django.conf import settings
|
|
8
7
|
from django.db import models
|
|
9
8
|
from django.forms.utils import ErrorDict, ErrorList
|
|
9
|
+
from django.utils.encoding import force_str
|
|
10
|
+
from django.utils.text import capfirst
|
|
10
11
|
import django_filters
|
|
11
12
|
from django_filters.constants import EMPTY_VALUES
|
|
12
|
-
from django_filters.utils import get_model_field, resolve_field
|
|
13
|
+
from django_filters.utils import get_model_field, label_for_filter, resolve_field, verbose_lookup_expr
|
|
13
14
|
from drf_spectacular.types import OpenApiTypes
|
|
14
15
|
from drf_spectacular.utils import extend_schema_field
|
|
15
16
|
|
|
@@ -123,8 +124,8 @@ class MultiValueUUIDFilter(django_filters.UUIDFilter, django_filters.MultipleCho
|
|
|
123
124
|
|
|
124
125
|
class RelatedMembershipBooleanFilter(django_filters.BooleanFilter):
|
|
125
126
|
"""
|
|
126
|
-
BooleanFilter for related objects that will explicitly perform `
|
|
127
|
-
|
|
127
|
+
BooleanFilter for related objects that will explicitly perform `isnull` lookups.
|
|
128
|
+
The `field_name` argument is required and must be set to the related field on the
|
|
128
129
|
model.
|
|
129
130
|
|
|
130
131
|
This should be used instead of a default `BooleanFilter` paired `method=`
|
|
@@ -132,15 +133,33 @@ class RelatedMembershipBooleanFilter(django_filters.BooleanFilter):
|
|
|
132
133
|
|
|
133
134
|
Example:
|
|
134
135
|
|
|
135
|
-
|
|
136
|
-
field_name="
|
|
137
|
-
label="Has
|
|
136
|
+
has_modules = RelatedMembershipBooleanFilter(
|
|
137
|
+
field_name="module_bays__installed_module",
|
|
138
|
+
label="Has modules",
|
|
138
139
|
)
|
|
140
|
+
|
|
141
|
+
This would generate a filter that returns instances that have at least one module
|
|
142
|
+
bay with an installed module. The `has_modules=False` filter would exclude instances
|
|
143
|
+
with at least one module bay with an installed module.
|
|
144
|
+
|
|
145
|
+
Set `exclude=True` to reverse the behavior of the filter. This __may__ be useful
|
|
146
|
+
for filtering on null directly related fields but this filter is not smart enough
|
|
147
|
+
to differentiate between `fieldA__fieldB__isnull` and `fieldA__isnull` so it's not
|
|
148
|
+
suitable for cases like the `has_empty_module_bays` filter where an instance may
|
|
149
|
+
not have any module bays.
|
|
150
|
+
|
|
151
|
+
See the below table for more information:
|
|
152
|
+
|
|
153
|
+
| value | exclude | Result
|
|
154
|
+
|-------------|------------------|-------
|
|
155
|
+
| True | False (default) | Return instances with at least one non-null match -- qs.filter(field_name__isnull=False)
|
|
156
|
+
| False | False (default) | Exclude instances with at least one non-null match -- qs.exclude(field_name__isnull=False)
|
|
157
|
+
| True | True | Return instances with at least one null match -- qs.filter(field_name__isnull=True)
|
|
158
|
+
| False | True | Exclude instances with at least one null match -- qs.exclude(field_name__isnull=True)
|
|
159
|
+
|
|
139
160
|
"""
|
|
140
161
|
|
|
141
|
-
def __init__(
|
|
142
|
-
self, field_name=None, lookup_expr="isnull", *, label=None, method=None, distinct=False, exclude=True, **kwargs
|
|
143
|
-
):
|
|
162
|
+
def __init__(self, field_name=None, lookup_expr="isnull", *, label=None, method=None, distinct=True, **kwargs):
|
|
144
163
|
if field_name is None:
|
|
145
164
|
raise ValueError(f"Field name is required for {self.__class__.__name__}")
|
|
146
165
|
|
|
@@ -150,11 +169,23 @@ class RelatedMembershipBooleanFilter(django_filters.BooleanFilter):
|
|
|
150
169
|
label=label,
|
|
151
170
|
method=method,
|
|
152
171
|
distinct=distinct,
|
|
153
|
-
exclude=exclude,
|
|
154
172
|
widget=forms.StaticSelect2(choices=forms.BOOLEAN_CHOICES),
|
|
155
173
|
**kwargs,
|
|
156
174
|
)
|
|
157
175
|
|
|
176
|
+
def filter(self, qs, value):
|
|
177
|
+
if value in EMPTY_VALUES:
|
|
178
|
+
return qs
|
|
179
|
+
if self.distinct:
|
|
180
|
+
qs = qs.distinct()
|
|
181
|
+
lookup = f"{self.field_name}__{self.lookup_expr}"
|
|
182
|
+
if bool(value):
|
|
183
|
+
# if self.exclude=False, return instances with field populated
|
|
184
|
+
return qs.filter(**{lookup: self.exclude})
|
|
185
|
+
else:
|
|
186
|
+
# if self.exclude=False, exclude instances with field populated
|
|
187
|
+
return qs.exclude(**{lookup: self.exclude})
|
|
188
|
+
|
|
158
189
|
|
|
159
190
|
class NumericArrayFilter(django_filters.NumberFilter):
|
|
160
191
|
"""
|
|
@@ -401,12 +432,22 @@ class NaturalKeyOrPKMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilte
|
|
|
401
432
|
Filter that supports filtering on values matching the `pk` field and another
|
|
402
433
|
field of a foreign-key related object. The desired field is set using the `to_field_name`
|
|
403
434
|
keyword argument on filter initialization (defaults to `name`).
|
|
435
|
+
|
|
436
|
+
NOTE that the `to_field_name` field does not have to be a "true" natural key (ie. unique), it
|
|
437
|
+
was just the best name we could come up with for this filter
|
|
438
|
+
|
|
404
439
|
"""
|
|
405
440
|
|
|
406
441
|
field_class = forms.MultiMatchModelMultipleChoiceField
|
|
407
442
|
|
|
408
|
-
def __init__(self, *args, **kwargs):
|
|
443
|
+
def __init__(self, *args, prefers_id=False, **kwargs):
|
|
444
|
+
"""Initialize the NaturalKeyOrPKMultipleChoiceFilter.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
prefers_id (bool, optional): Prefer PK (ID) over the 'to_field_name'. Defaults to False.
|
|
448
|
+
"""
|
|
409
449
|
self.natural_key = kwargs.setdefault("to_field_name", "name")
|
|
450
|
+
self.prefers_id = prefers_id
|
|
410
451
|
super().__init__(*args, **kwargs)
|
|
411
452
|
|
|
412
453
|
def get_filter_predicate(self, v):
|
|
@@ -681,6 +722,18 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
681
722
|
# Of course setting the negation of the existing filter's exclude attribute handles both cases
|
|
682
723
|
new_filter.exclude = not filter_field.exclude
|
|
683
724
|
|
|
725
|
+
# If the base filter_field has a custom label, django_filters won't adjust it for the new_filter lookup,
|
|
726
|
+
# so we have to do it.
|
|
727
|
+
if filter_field.label and filter_field.label != label_for_filter(
|
|
728
|
+
cls.Meta.model, filter_field.field_name, filter_field.lookup_expr, filter_field.exclude
|
|
729
|
+
):
|
|
730
|
+
# Lightly adjusted from label_for_filter() implementation:
|
|
731
|
+
verbose_expression = ["exclude", filter_field.label] if new_filter.exclude else [filter_field.label]
|
|
732
|
+
if isinstance(lookup_expr, str):
|
|
733
|
+
verbose_expression.append(verbose_lookup_expr(lookup_expr))
|
|
734
|
+
verbose_expression = [force_str(part) for part in verbose_expression if part]
|
|
735
|
+
new_filter.label = capfirst(" ".join(verbose_expression))
|
|
736
|
+
|
|
684
737
|
magic_filters[new_filter_name] = new_filter
|
|
685
738
|
|
|
686
739
|
return magic_filters
|
|
@@ -711,8 +764,8 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
711
764
|
def get_fields(cls):
|
|
712
765
|
fields = super().get_fields()
|
|
713
766
|
if "id" not in fields and (cls._meta.exclude is None or "id" not in cls._meta.exclude):
|
|
714
|
-
# Add "id" as the first key in the `fields`
|
|
715
|
-
fields =
|
|
767
|
+
# Add "id" as the first key in the `fields` dict
|
|
768
|
+
fields = {"id": [django_filters.conf.settings.DEFAULT_LOOKUP_EXPR], **fields}
|
|
716
769
|
return fields
|
|
717
770
|
|
|
718
771
|
@classmethod
|
|
@@ -727,8 +780,51 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
727
780
|
if filter_name.startswith("_"):
|
|
728
781
|
del filters[filter_name]
|
|
729
782
|
|
|
783
|
+
if getattr(cls._meta.model, "is_contact_associable_model", False):
|
|
784
|
+
# Add "contacts" and "teams" filters
|
|
785
|
+
from nautobot.extras.models import Contact, Team
|
|
786
|
+
|
|
787
|
+
if "contacts" not in filters:
|
|
788
|
+
filters["contacts"] = NaturalKeyOrPKMultipleChoiceFilter(
|
|
789
|
+
queryset=Contact.objects.all(),
|
|
790
|
+
field_name="associated_contacts__contact",
|
|
791
|
+
to_field_name="name",
|
|
792
|
+
label="Contacts (name or ID)",
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
if "teams" not in filters:
|
|
796
|
+
filters["teams"] = NaturalKeyOrPKMultipleChoiceFilter(
|
|
797
|
+
queryset=Team.objects.all(),
|
|
798
|
+
field_name="associated_contacts__team",
|
|
799
|
+
to_field_name="name",
|
|
800
|
+
label="Teams (name or ID)",
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False):
|
|
804
|
+
if not hasattr(cls._meta.model, "static_group_association_set"):
|
|
805
|
+
logger.warning(
|
|
806
|
+
"Model %s has 'is_dynamic_group_associable_model = True' but lacks "
|
|
807
|
+
"a 'static_group_association_set' attribute. Perhaps this is due to it inheriting from "
|
|
808
|
+
"the deprecated DynamicGroupMixin class instead of the preferred DynamicGroupsModelMixin?",
|
|
809
|
+
cls._meta.model,
|
|
810
|
+
)
|
|
811
|
+
else:
|
|
812
|
+
# Add "dynamic_groups" field as the last key
|
|
813
|
+
from nautobot.extras.models import DynamicGroup
|
|
814
|
+
|
|
815
|
+
filters["dynamic_groups"] = NaturalKeyOrPKMultipleChoiceFilter(
|
|
816
|
+
queryset=DynamicGroup.objects.all(),
|
|
817
|
+
field_name="static_group_association_set__dynamic_group",
|
|
818
|
+
to_field_name="name",
|
|
819
|
+
query_params={"content_type": cls._meta.model._meta.label_lower},
|
|
820
|
+
label="Dynamic groups (name or ID)",
|
|
821
|
+
)
|
|
822
|
+
|
|
730
823
|
# django-filters has no concept of "abstract" filtersets, so we have to fake it
|
|
731
824
|
if cls._meta.model is not None:
|
|
825
|
+
if "tags" in filters and isinstance(filters["tags"], TagFilter):
|
|
826
|
+
filters["tags"].extra["query_params"] = {"content_types": [cls._meta.model._meta.label_lower]}
|
|
827
|
+
|
|
732
828
|
new_filters = {}
|
|
733
829
|
for existing_filter_name, existing_filter in filters.items():
|
|
734
830
|
new_filters.update(
|
nautobot/core/forms/fields.py
CHANGED
|
@@ -228,6 +228,10 @@ class MultipleContentTypeField(django_forms.ModelMultipleChoiceField):
|
|
|
228
228
|
|
|
229
229
|
if "queryset" not in kwargs:
|
|
230
230
|
if feature is not None:
|
|
231
|
+
from nautobot.extras.registry import registry
|
|
232
|
+
|
|
233
|
+
if feature not in registry["model_features"]:
|
|
234
|
+
raise KeyError
|
|
231
235
|
kwargs["queryset"] = ContentType.objects.filter(
|
|
232
236
|
extras_utils.FeatureQuery(feature).get_query()
|
|
233
237
|
).order_by("app_label", "model")
|
|
@@ -453,6 +457,7 @@ class DynamicModelChoiceMixin:
|
|
|
453
457
|
|
|
454
458
|
filter = django_filters.ModelChoiceFilter # 2.0 TODO(Glenn): can we rename this? pylint: disable=redefined-builtin
|
|
455
459
|
widget = widgets.APISelect
|
|
460
|
+
iterator = widgets.MinimalModelChoiceIterator
|
|
456
461
|
|
|
457
462
|
def __init__(
|
|
458
463
|
self,
|
|
@@ -475,6 +480,7 @@ class DynamicModelChoiceMixin:
|
|
|
475
480
|
# to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference
|
|
476
481
|
# by widget_attrs()
|
|
477
482
|
self.to_field_name = kwargs.get("to_field_name")
|
|
483
|
+
self.data_queryset = kwargs.get("queryset") # may be updated in get_bound_field()
|
|
478
484
|
|
|
479
485
|
super().__init__(*args, **kwargs)
|
|
480
486
|
|
|
@@ -539,12 +545,12 @@ class DynamicModelChoiceMixin:
|
|
|
539
545
|
field_name = getattr(self, "to_field_name") or "pk"
|
|
540
546
|
filter_ = self.filter(field_name=field_name)
|
|
541
547
|
try:
|
|
542
|
-
self.
|
|
543
|
-
except (TypeError, ValidationError):
|
|
548
|
+
self.data_queryset = filter_.filter(self.queryset, data)
|
|
549
|
+
except (TypeError, ValueError, ValidationError):
|
|
544
550
|
# Catch any error caused by invalid initial data passed from the user
|
|
545
|
-
self.
|
|
551
|
+
self.data_queryset = self.queryset.none()
|
|
546
552
|
else:
|
|
547
|
-
self.
|
|
553
|
+
self.data_queryset = self.queryset.none()
|
|
548
554
|
|
|
549
555
|
# Set the data URL on the APISelect widget (if not already set)
|
|
550
556
|
widget = bound_field.field.widget
|
nautobot/core/forms/forms.py
CHANGED
|
@@ -59,6 +59,13 @@ class AddressFieldMixin(forms.ModelForm):
|
|
|
59
59
|
class BootstrapMixin(forms.BaseForm):
|
|
60
60
|
"""
|
|
61
61
|
Add the base Bootstrap CSS classes to form elements.
|
|
62
|
+
|
|
63
|
+
Note that this only applies to form fields that are:
|
|
64
|
+
|
|
65
|
+
1. statically defined on the form class at declaration time, or
|
|
66
|
+
2. dynamically added to the form at init time by a class **later in the MRO than this mixin**.
|
|
67
|
+
|
|
68
|
+
If a class earlier in the MRO adds its own fields, it will have to ensure that the widgets are correctly configured.
|
|
62
69
|
"""
|
|
63
70
|
|
|
64
71
|
def __init__(self, *args, **kwargs):
|
|
@@ -73,8 +80,9 @@ class BootstrapMixin(forms.BaseForm):
|
|
|
73
80
|
|
|
74
81
|
for field in self.fields.values():
|
|
75
82
|
if field.widget.__class__ not in exempt_widgets:
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
css_classes = field.widget.attrs.get("class", "")
|
|
84
|
+
if "form-control" not in css_classes:
|
|
85
|
+
field.widget.attrs["class"] = " ".join([css_classes, "form-control"]).strip()
|
|
78
86
|
if field.required and not isinstance(field.widget, forms.FileInput):
|
|
79
87
|
field.widget.attrs["required"] = "required"
|
|
80
88
|
if "placeholder" not in field.widget.attrs:
|
|
@@ -121,7 +129,7 @@ class BulkRenameForm(forms.Form):
|
|
|
121
129
|
"""
|
|
122
130
|
|
|
123
131
|
find = forms.CharField()
|
|
124
|
-
replace = forms.CharField()
|
|
132
|
+
replace = forms.CharField(required=False, strip=False)
|
|
125
133
|
use_regex = forms.BooleanField(required=False, initial=True, label="Use regular expressions")
|
|
126
134
|
|
|
127
135
|
def clean(self):
|
nautobot/core/forms/widgets.py
CHANGED
|
@@ -116,6 +116,23 @@ class ContentTypeSelect(StaticSelect2):
|
|
|
116
116
|
option_template_name = "widgets/select_contenttype.html"
|
|
117
117
|
|
|
118
118
|
|
|
119
|
+
class MinimalModelChoiceIterator(ModelChoiceIterator):
|
|
120
|
+
"""
|
|
121
|
+
Helper class for APISelect and APISelectMultiple.
|
|
122
|
+
|
|
123
|
+
Allows the widget to keep a full `queryset` for data validation, but, for performance reasons, returns a minimal
|
|
124
|
+
subset of choices at render time derived from the widget's `data_queryset`.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def queryset(self):
|
|
129
|
+
return self.field.data_queryset
|
|
130
|
+
|
|
131
|
+
@queryset.setter
|
|
132
|
+
def queryset(self, value):
|
|
133
|
+
return self.field.data_queryset
|
|
134
|
+
|
|
135
|
+
|
|
119
136
|
class APISelect(SelectWithDisabled):
|
|
120
137
|
"""
|
|
121
138
|
A select widget populated via an API call
|
|
@@ -173,7 +190,7 @@ class APISelect(SelectWithDisabled):
|
|
|
173
190
|
and isinstance(self.choices, Iterable)
|
|
174
191
|
):
|
|
175
192
|
|
|
176
|
-
class ModelChoiceIteratorWithNullOption(
|
|
193
|
+
class ModelChoiceIteratorWithNullOption(MinimalModelChoiceIterator):
|
|
177
194
|
def __init__(self, *args, **kwargs):
|
|
178
195
|
self.null_options = kwargs.pop("null_option", None)
|
|
179
196
|
super().__init__(*args, **kwargs)
|
|
@@ -64,8 +64,8 @@ def generate_filter_resolver(schema_type, resolver_name, field_name):
|
|
|
64
64
|
"""
|
|
65
65
|
filterset_class = schema_type._meta.filterset_class
|
|
66
66
|
|
|
67
|
-
def resolve_filter(self,
|
|
68
|
-
if not filterset_class:
|
|
67
|
+
def resolve_filter(self, info, **kwargs):
|
|
68
|
+
if not filterset_class or not kwargs:
|
|
69
69
|
return getattr(self, field_name).all()
|
|
70
70
|
|
|
71
71
|
# Inverse of substitution logic from get_filtering_args_from_filterset() - transform "_type" back to "type"
|
nautobot/core/graphql/schema.py
CHANGED
|
@@ -4,6 +4,7 @@ from collections import OrderedDict
|
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
6
|
from django.conf import settings
|
|
7
|
+
from django.contrib.contenttypes.fields import GenericRelation
|
|
7
8
|
from django.contrib.contenttypes.models import ContentType
|
|
8
9
|
from django.core.validators import ValidationError
|
|
9
10
|
from django.db.models import ManyToManyField
|
|
@@ -42,7 +43,7 @@ from nautobot.dcim.graphql.types import (
|
|
|
42
43
|
RearPortType,
|
|
43
44
|
)
|
|
44
45
|
from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipSideChoices
|
|
45
|
-
from nautobot.extras.graphql.types import DynamicGroupType, TagType
|
|
46
|
+
from nautobot.extras.graphql.types import ContactAssociationType, DynamicGroupType, TagType
|
|
46
47
|
from nautobot.extras.models import ComputedField, CustomField, Relationship
|
|
47
48
|
from nautobot.extras.registry import registry
|
|
48
49
|
from nautobot.extras.utils import check_if_key_is_graphql_safe
|
|
@@ -68,8 +69,9 @@ registry["graphql_types"]["dcim.powerport"] = PowerPortType
|
|
|
68
69
|
registry["graphql_types"]["dcim.rack"] = RackType
|
|
69
70
|
registry["graphql_types"]["dcim.rearport"] = RearPortType
|
|
70
71
|
registry["graphql_types"]["dcim.location"] = LocationType
|
|
71
|
-
registry["graphql_types"]["extras.
|
|
72
|
+
registry["graphql_types"]["extras.contactassociation"] = ContactAssociationType
|
|
72
73
|
registry["graphql_types"]["extras.dynamicgroup"] = DynamicGroupType
|
|
74
|
+
registry["graphql_types"]["extras.tag"] = TagType
|
|
73
75
|
registry["graphql_types"]["ipam.ipaddress"] = IPAddressType
|
|
74
76
|
registry["graphql_types"]["ipam.prefix"] = PrefixType
|
|
75
77
|
registry["graphql_types"]["ipam.vlan"] = VLANType
|
|
@@ -127,6 +129,11 @@ def extend_schema_type(schema_type):
|
|
|
127
129
|
#
|
|
128
130
|
schema_type = extend_schema_type_config_context(schema_type, model)
|
|
129
131
|
|
|
132
|
+
#
|
|
133
|
+
# Global features (contacts, teams, dynamic groups)
|
|
134
|
+
#
|
|
135
|
+
schema_type = extend_schema_type_global_features(schema_type, model)
|
|
136
|
+
|
|
130
137
|
#
|
|
131
138
|
# Relationships
|
|
132
139
|
#
|
|
@@ -199,8 +206,7 @@ def extend_schema_type_filter(schema_type, model):
|
|
|
199
206
|
(DjangoObjectType): The extended schema_type object
|
|
200
207
|
"""
|
|
201
208
|
for field in model._meta.get_fields():
|
|
202
|
-
|
|
203
|
-
if not isinstance(field, (ManyToManyField, ManyToManyRel, ManyToOneRel)):
|
|
209
|
+
if not isinstance(field, (ManyToManyField, ManyToManyRel, ManyToOneRel, GenericRelation)):
|
|
204
210
|
continue
|
|
205
211
|
# OneToOneRel is a subclass of ManyToOneRel, but we don't want to treat it as a list
|
|
206
212
|
if isinstance(field, OneToOneRel):
|
|
@@ -366,14 +372,29 @@ def extend_schema_type_config_context(schema_type, model):
|
|
|
366
372
|
return schema_type
|
|
367
373
|
|
|
368
374
|
|
|
375
|
+
def extend_schema_type_global_features(schema_type, model):
|
|
376
|
+
"""
|
|
377
|
+
Extend schema_type object to have attributes and resolvers for global features (dynamic groups, etc.).
|
|
378
|
+
"""
|
|
379
|
+
# associated_contacts and associated_object_metadata are handled elsewhere by extend_schema_type_filter()
|
|
380
|
+
if getattr(model, "is_dynamic_group_associable_model", False):
|
|
381
|
+
|
|
382
|
+
def resolve_dynamic_groups(self, args):
|
|
383
|
+
return self.dynamic_groups
|
|
384
|
+
|
|
385
|
+
setattr(schema_type, "resolve_dynamic_groups", resolve_dynamic_groups)
|
|
386
|
+
schema_type._meta.fields["dynamic_groups"] = graphene.Field.mounted(graphene.List(DynamicGroupType))
|
|
387
|
+
|
|
388
|
+
return schema_type
|
|
389
|
+
|
|
390
|
+
|
|
369
391
|
def extend_schema_type_relationships(schema_type, model):
|
|
370
392
|
"""Extend the schema type with attributes and resolvers corresponding
|
|
371
393
|
to the relationships associated with this model."""
|
|
372
394
|
|
|
373
|
-
ct = ContentType.objects.get_for_model(model)
|
|
374
395
|
relationships_by_side = {
|
|
375
|
-
"source": Relationship.objects.
|
|
376
|
-
"destination": Relationship.objects.
|
|
396
|
+
"source": Relationship.objects.get_for_model_source(model),
|
|
397
|
+
"destination": Relationship.objects.get_for_model_destination(model),
|
|
377
398
|
}
|
|
378
399
|
|
|
379
400
|
prefix = ""
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import codecs
|
|
1
2
|
import contextlib
|
|
2
3
|
from io import BytesIO
|
|
3
4
|
|
|
@@ -14,6 +15,8 @@ from nautobot.core.api.renderers import NautobotCSVRenderer
|
|
|
14
15
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
15
16
|
from nautobot.core.celery import app, register_jobs
|
|
16
17
|
from nautobot.core.exceptions import AbortTransaction
|
|
18
|
+
from nautobot.core.jobs.cleanup import LogsCleanup
|
|
19
|
+
from nautobot.core.jobs.groups import RefreshDynamicGroupCaches
|
|
17
20
|
from nautobot.core.utils.lookup import get_filterset_for_model
|
|
18
21
|
from nautobot.core.utils.requests import get_filterable_params_from_filter_params
|
|
19
22
|
from nautobot.extras.datasources import ensure_git_repository, git_repository_dry_run, refresh_datasource_content
|
|
@@ -36,6 +39,7 @@ class GitRepositorySync(Job):
|
|
|
36
39
|
|
|
37
40
|
class Meta:
|
|
38
41
|
name = "Git Repository: Sync"
|
|
42
|
+
description = "Clone and/or pull a Git repository, then refresh data sourced from this repository."
|
|
39
43
|
has_sensitive_variables = False
|
|
40
44
|
|
|
41
45
|
def run(self, repository):
|
|
@@ -65,6 +69,7 @@ class GitRepositoryDryRun(Job):
|
|
|
65
69
|
|
|
66
70
|
class Meta:
|
|
67
71
|
name = "Git Repository: Dry-Run"
|
|
72
|
+
description = "Dry run of Git repository sync - will not update data sourced from this repository."
|
|
68
73
|
has_sensitive_variables = False
|
|
69
74
|
|
|
70
75
|
def run(self, repository):
|
|
@@ -111,6 +116,7 @@ class ExportObjectList(Job):
|
|
|
111
116
|
|
|
112
117
|
class Meta:
|
|
113
118
|
name = "Export Object List"
|
|
119
|
+
description = "Export a list of objects to CSV or YAML, or render a specified Export Template."
|
|
114
120
|
has_sensitive_variables = False
|
|
115
121
|
# Exporting large querysets may take substantial processing time
|
|
116
122
|
soft_time_limit = 1800
|
|
@@ -135,7 +141,15 @@ class ExportObjectList(Job):
|
|
|
135
141
|
# such that they never are even seen here.
|
|
136
142
|
query_params = QueryDict(query_string)
|
|
137
143
|
self.logger.debug("Parsed query_params: `%s`", query_params.dict())
|
|
138
|
-
default_non_filter_params = (
|
|
144
|
+
default_non_filter_params = (
|
|
145
|
+
"all_filters_removed",
|
|
146
|
+
"export",
|
|
147
|
+
"page",
|
|
148
|
+
"per_page",
|
|
149
|
+
"saved_view",
|
|
150
|
+
"sort",
|
|
151
|
+
"table_changes_pending",
|
|
152
|
+
)
|
|
139
153
|
filter_params = get_filterable_params_from_filter_params(
|
|
140
154
|
query_params, default_non_filter_params, filterset_class()
|
|
141
155
|
)
|
|
@@ -212,6 +226,7 @@ class ImportObjects(Job):
|
|
|
212
226
|
|
|
213
227
|
class Meta:
|
|
214
228
|
name = "Import Objects"
|
|
229
|
+
description = "Import objects from CSV-formatted data."
|
|
215
230
|
has_sensitive_variables = False
|
|
216
231
|
# Importing large files may take substantial processing time
|
|
217
232
|
soft_time_limit = 1800
|
|
@@ -282,7 +297,9 @@ class ImportObjects(Job):
|
|
|
282
297
|
if not csv_data and not csv_file:
|
|
283
298
|
raise RunJobTaskFailed("Either csv_data or csv_file must be provided")
|
|
284
299
|
if csv_file:
|
|
285
|
-
|
|
300
|
+
# data_encoding is utf-8 and file_encoding is utf-8-sig
|
|
301
|
+
# Bytes read from the original file are decoded according to file_encoding, and the result is encoded using data_encoding.
|
|
302
|
+
csv_bytes = codecs.EncodedFile(csv_file, "utf-8", "utf-8-sig")
|
|
286
303
|
else:
|
|
287
304
|
csv_bytes = BytesIO(csv_data.encode("utf-8"))
|
|
288
305
|
|
|
@@ -314,5 +331,5 @@ class ImportObjects(Job):
|
|
|
314
331
|
raise RunJobTaskFailed("CSV import not fully successful, see logs")
|
|
315
332
|
|
|
316
333
|
|
|
317
|
-
jobs = [ExportObjectList, GitRepositorySync, GitRepositoryDryRun, ImportObjects]
|
|
334
|
+
jobs = [ExportObjectList, GitRepositorySync, GitRepositoryDryRun, ImportObjects, LogsCleanup, RefreshDynamicGroupCaches]
|
|
318
335
|
register_jobs(*jobs)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from datetime import timedelta
|
|
2
|
+
|
|
3
|
+
from django.core.exceptions import PermissionDenied
|
|
4
|
+
from django.db.models.signals import pre_delete
|
|
5
|
+
from django.utils import timezone
|
|
6
|
+
|
|
7
|
+
from nautobot.core.choices import ChoiceSet
|
|
8
|
+
from nautobot.core.utils.config import get_settings_or_config
|
|
9
|
+
from nautobot.extras.jobs import IntegerVar, Job, MultiChoiceVar
|
|
10
|
+
from nautobot.extras.models import JobResult, ObjectChange
|
|
11
|
+
from nautobot.extras.signals import _handle_deleted_object
|
|
12
|
+
|
|
13
|
+
name = "System Jobs"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CleanupTypes(ChoiceSet):
|
|
17
|
+
JOB_RESULT = "extras.JobResult"
|
|
18
|
+
OBJECT_CHANGE = "extras.ObjectChange"
|
|
19
|
+
|
|
20
|
+
CHOICES = (
|
|
21
|
+
(JOB_RESULT, "Job results"),
|
|
22
|
+
(OBJECT_CHANGE, "Change logs"),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LogsCleanup(Job):
|
|
27
|
+
"""
|
|
28
|
+
System job to clean up ObjectChange and/or JobResult (and JobLogEntry) records older than a given age.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
cleanup_types = MultiChoiceVar(
|
|
32
|
+
choices=CleanupTypes.CHOICES,
|
|
33
|
+
required=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
max_age = IntegerVar(
|
|
37
|
+
description=(
|
|
38
|
+
"Maximum age of records to retain, in days. "
|
|
39
|
+
"Leave empty to use the CHANGELOG_RETENTION setting as the maximum."
|
|
40
|
+
),
|
|
41
|
+
label="Max Age",
|
|
42
|
+
min_value=0,
|
|
43
|
+
required=False,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
class Meta:
|
|
47
|
+
name = "Logs Cleanup"
|
|
48
|
+
description = "Delete ObjectChange and/or JobResult/JobLogEntry records older than a specified cutoff."
|
|
49
|
+
has_sensitive_variables = False
|
|
50
|
+
|
|
51
|
+
def run(self, *, cleanup_types, max_age=None):
|
|
52
|
+
if max_age in (None, ""):
|
|
53
|
+
max_age = get_settings_or_config("CHANGELOG_RETENTION")
|
|
54
|
+
if max_age == 0:
|
|
55
|
+
self.logger.warning(
|
|
56
|
+
"CHANGELOG_RETENTION setting is set to zero, disabling this Job. "
|
|
57
|
+
"If you wish to use this Job to delete records, you must specify a `max_age` value."
|
|
58
|
+
)
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
if CleanupTypes.JOB_RESULT in cleanup_types and not self.user.has_perm("extras.delete_jobresult"):
|
|
62
|
+
self.logger.error('User "%s" does not have permission to delete JobResult records', self.user)
|
|
63
|
+
raise PermissionDenied("User does not have delete permissions for JobResult records")
|
|
64
|
+
|
|
65
|
+
if CleanupTypes.OBJECT_CHANGE in cleanup_types and not self.user.has_perm("extras.delete_objectchange"):
|
|
66
|
+
self.logger.error('User "%s" does not have permission to delete ObjectChange records', self.user)
|
|
67
|
+
raise PermissionDenied("User does not have delete permissions for ObjectChange records")
|
|
68
|
+
|
|
69
|
+
# Bulk delete goes much faster if Django doesn't have signals to process.
|
|
70
|
+
# Temporarily detach the ones we *know* to be irrelevant.
|
|
71
|
+
self.logger.debug("Temporarily disconnecting some signals for performance")
|
|
72
|
+
pre_delete.disconnect(_handle_deleted_object)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
cutoff = timezone.now() - timedelta(days=max_age)
|
|
76
|
+
result = {}
|
|
77
|
+
|
|
78
|
+
if CleanupTypes.JOB_RESULT in cleanup_types:
|
|
79
|
+
self.logger.info("Deleting JobResult records prior to %s", cutoff)
|
|
80
|
+
_, deleted_dict = JobResult.objects.restrict(self.user, "delete").filter(date_done__lt=cutoff).delete()
|
|
81
|
+
result.setdefault("extras.JobResult", 0)
|
|
82
|
+
result.setdefault("extras.JobLogEntry", 0)
|
|
83
|
+
result.update(**deleted_dict)
|
|
84
|
+
self.logger.info(
|
|
85
|
+
"Deleted %d JobResult records and their associated %d JobLogEntry records",
|
|
86
|
+
result["extras.JobResult"],
|
|
87
|
+
result["extras.JobLogEntry"],
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if CleanupTypes.OBJECT_CHANGE in cleanup_types:
|
|
91
|
+
self.logger.info("Deleting ObjectChange records prior to %s", cutoff)
|
|
92
|
+
deleted_count, _ = ObjectChange.objects.restrict(self.user, "delete").filter(time__lt=cutoff).delete()
|
|
93
|
+
self.logger.info("Deleted %d ObjectChange records", deleted_count)
|
|
94
|
+
result["extras.ObjectChange"] = deleted_count
|
|
95
|
+
|
|
96
|
+
return result
|
|
97
|
+
finally:
|
|
98
|
+
# Be sure to clean up after ourselves!
|
|
99
|
+
self.logger.debug("Re-connecting signals")
|
|
100
|
+
pre_delete.connect(_handle_deleted_object)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from nautobot.extras.choices import DynamicGroupTypeChoices
|
|
2
|
+
from nautobot.extras.jobs import Job, ObjectVar
|
|
3
|
+
from nautobot.extras.models import DynamicGroup
|
|
4
|
+
|
|
5
|
+
name = "System Jobs"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RefreshDynamicGroupCaches(Job):
|
|
9
|
+
"""
|
|
10
|
+
System job to recalculate and re-cache the members of Dynamic Groups for improved performance.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
single_group = ObjectVar(
|
|
14
|
+
description="Select to refresh only a single specified group instead of all groups",
|
|
15
|
+
model=DynamicGroup,
|
|
16
|
+
query_params={
|
|
17
|
+
"group_type": [DynamicGroupTypeChoices.TYPE_DYNAMIC_FILTER, DynamicGroupTypeChoices.TYPE_DYNAMIC_SET],
|
|
18
|
+
},
|
|
19
|
+
required=False,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
class Meta:
|
|
23
|
+
name = "Refresh Dynamic Group Caches"
|
|
24
|
+
description = "Re-calculate and re-cache the membership lists of Dynamic Groups."
|
|
25
|
+
has_sensitive_variables = False
|
|
26
|
+
|
|
27
|
+
def run(self, single_group=None):
|
|
28
|
+
groups = DynamicGroup.objects.restrict(self.user, "view").exclude(
|
|
29
|
+
group_type=DynamicGroupTypeChoices.TYPE_STATIC
|
|
30
|
+
)
|
|
31
|
+
if single_group is not None:
|
|
32
|
+
groups = groups.filter(pk=single_group.pk)
|
|
33
|
+
|
|
34
|
+
for group in groups:
|
|
35
|
+
group.update_cached_members()
|
|
36
|
+
self.logger.info("Cache refreshed successfully, now with %d members", group.count, extra={"object": group})
|
|
37
|
+
|
|
38
|
+
self.logger.info("Cache(s) refreshed")
|