nautobot 2.2.9__py3-none-any.whl → 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/forms.py +4 -0
- nautobot/apps/models.py +10 -1
- nautobot/circuits/__init__.py +0 -1
- nautobot/circuits/apps.py +1 -0
- nautobot/circuits/factory.py +15 -3
- nautobot/circuits/filters.py +13 -0
- nautobot/circuits/forms.py +13 -0
- nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
- nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
- nautobot/circuits/models.py +16 -3
- nautobot/circuits/tables.py +16 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
- nautobot/circuits/tests/test_api.py +7 -5
- nautobot/circuits/tests/test_filters.py +12 -5
- nautobot/circuits/tests/test_models.py +33 -2
- nautobot/circuits/views.py +2 -3
- nautobot/cloud/__init__.py +0 -0
- nautobot/cloud/api/__init__.py +0 -0
- nautobot/cloud/api/serializers.py +54 -0
- nautobot/cloud/api/urls.py +16 -0
- nautobot/cloud/api/views.py +48 -0
- nautobot/cloud/apps.py +13 -0
- nautobot/cloud/factory.py +113 -0
- nautobot/cloud/filters.py +187 -0
- nautobot/cloud/forms.py +339 -0
- nautobot/cloud/homepage.py +43 -0
- nautobot/cloud/migrations/0001_initial.py +304 -0
- nautobot/cloud/migrations/__init__.py +0 -0
- nautobot/cloud/models.py +246 -0
- nautobot/cloud/navigation.py +85 -0
- nautobot/cloud/tables.py +157 -0
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +122 -0
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +111 -0
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +69 -0
- nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
- nautobot/cloud/tests/__init__.py +0 -0
- nautobot/cloud/tests/test_api.py +248 -0
- nautobot/cloud/tests/test_filters.py +125 -0
- nautobot/cloud/tests/test_models.py +43 -0
- nautobot/cloud/tests/test_views.py +153 -0
- nautobot/cloud/urls.py +14 -0
- nautobot/cloud/views.py +181 -0
- nautobot/core/__init__.py +0 -3
- nautobot/core/api/metadata.py +1 -0
- nautobot/core/api/parsers.py +7 -1
- nautobot/core/api/urls.py +1 -0
- nautobot/core/api/utils.py +1 -0
- nautobot/core/api/views.py +4 -0
- nautobot/core/apps/__init__.py +6 -3
- nautobot/core/constants.py +8 -0
- nautobot/core/factory.py +32 -1
- nautobot/core/filters.py +95 -13
- nautobot/core/forms/fields.py +10 -4
- nautobot/core/forms/forms.py +11 -3
- nautobot/core/forms/widgets.py +18 -1
- nautobot/core/graphql/schema.py +26 -4
- nautobot/core/jobs/__init__.py +16 -2
- nautobot/core/jobs/cleanup.py +100 -0
- nautobot/core/jobs/groups.py +38 -0
- nautobot/core/management/commands/generate_test_data.py +116 -3
- nautobot/core/models/__init__.py +34 -9
- nautobot/core/models/generics.py +19 -3
- nautobot/core/models/name_color_content_types.py +7 -28
- nautobot/core/models/querysets.py +4 -3
- nautobot/core/models/tree_queries.py +1 -1
- nautobot/core/models/utils.py +21 -5
- nautobot/core/settings.py +2 -17
- nautobot/core/settings.yaml +34 -13
- nautobot/core/settings_funcs.py +103 -0
- nautobot/core/tables.py +130 -56
- nautobot/core/templates/admin/search_form.html +1 -1
- nautobot/core/templates/buttons/add.html +11 -3
- nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
- nautobot/core/templates/buttons/export.html +101 -53
- nautobot/core/templates/buttons/job_import.html +11 -3
- nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
- nautobot/core/templates/generic/object_bulk_update.html +3 -1
- nautobot/core/templates/generic/object_changelog.html +0 -9
- nautobot/core/templates/generic/object_list.html +156 -17
- nautobot/core/templates/generic/object_retrieve.html +80 -16
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
- nautobot/core/templates/inc/javascript.html +2 -0
- nautobot/core/templates/inc/media.html +2 -2
- nautobot/core/templates/inc/nav_menu.html +1 -0
- nautobot/core/templates/inc/paginator.html +7 -7
- nautobot/core/templates/inc/search_panel.html +2 -2
- nautobot/core/templates/inc/table.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +13 -8
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
- nautobot/core/templates/utilities/theme_preview.html +25 -8
- nautobot/core/templates/utilities/worker_status.html +152 -0
- nautobot/core/templatetags/buttons.py +335 -38
- nautobot/core/templatetags/form_helpers.py +1 -1
- nautobot/core/templatetags/helpers.py +181 -11
- nautobot/core/testing/api.py +5 -4
- nautobot/core/testing/filters.py +63 -14
- nautobot/core/testing/mixins.py +46 -0
- nautobot/core/testing/models.py +22 -0
- nautobot/core/testing/schema.py +4 -8
- nautobot/core/testing/views.py +31 -14
- nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
- nautobot/core/tests/integration/test_swagger.py +1 -1
- nautobot/core/tests/nautobot_config.py +0 -1
- nautobot/core/tests/runner.py +2 -2
- nautobot/core/tests/test_api.py +1 -0
- nautobot/core/tests/test_authentication.py +7 -2
- nautobot/core/tests/test_filters.py +11 -9
- nautobot/core/tests/test_forms.py +9 -0
- nautobot/core/tests/test_graphql.py +27 -16
- nautobot/core/tests/test_jobs.py +122 -0
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +12 -5
- nautobot/core/tests/test_utils.py +31 -20
- nautobot/core/tests/test_views.py +6 -6
- nautobot/core/urls.py +8 -3
- nautobot/core/utils/deprecation.py +29 -0
- nautobot/core/utils/filtering.py +12 -9
- nautobot/core/utils/lookup.py +37 -2
- nautobot/core/utils/requests.py +4 -1
- nautobot/core/views/__init__.py +137 -24
- nautobot/core/views/generic.py +119 -67
- nautobot/core/views/mixins.py +105 -36
- nautobot/core/views/paginator.py +9 -3
- nautobot/core/views/renderers.py +121 -56
- nautobot/core/views/utils.py +81 -1
- nautobot/dcim/__init__.py +0 -1
- nautobot/dcim/api/serializers.py +180 -44
- nautobot/dcim/api/urls.py +7 -3
- nautobot/dcim/api/views.py +53 -7
- nautobot/dcim/apps.py +3 -0
- nautobot/dcim/choices.py +25 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/factory.py +252 -18
- nautobot/dcim/filters/__init__.py +373 -193
- nautobot/dcim/filters/mixins.py +274 -1
- nautobot/dcim/forms.py +834 -121
- nautobot/dcim/graphql/types.py +2 -2
- nautobot/dcim/homepage.py +1 -1
- nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
- nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
- nautobot/dcim/migrations/0061_module_models.py +862 -0
- nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
- nautobot/dcim/models/__init__.py +8 -0
- nautobot/dcim/models/cables.py +15 -0
- nautobot/dcim/models/device_component_templates.py +207 -53
- nautobot/dcim/models/device_components.py +275 -99
- nautobot/dcim/models/devices.py +468 -13
- nautobot/dcim/models/racks.py +0 -1
- nautobot/dcim/navigation.py +47 -0
- nautobot/dcim/signals.py +3 -3
- nautobot/dcim/tables/__init__.py +35 -23
- nautobot/dcim/tables/devices.py +229 -43
- nautobot/dcim/tables/devicetypes.py +65 -9
- nautobot/dcim/tables/racks.py +5 -1
- nautobot/dcim/tables/template_code.py +46 -26
- nautobot/dcim/templates/dcim/cable_connect.html +76 -3
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/device/base.html +14 -6
- nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
- nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
- nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
- nautobot/dcim/templates/dcim/device/frontports.html +2 -3
- nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
- nautobot/dcim/templates/dcim/device/inventory.html +2 -3
- nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
- nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
- nautobot/dcim/templates/dcim/device/powerports.html +2 -3
- nautobot/dcim/templates/dcim/device/rearports.html +2 -3
- nautobot/dcim/templates/dcim/device.html +45 -1
- nautobot/dcim/templates/dcim/device_component.html +13 -5
- nautobot/dcim/templates/dcim/device_list.html +2 -1
- nautobot/dcim/templates/dcim/devicetype.html +99 -98
- nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
- nautobot/dcim/templates/dcim/interface.html +17 -2
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/interface_edit.html +1 -0
- nautobot/dcim/templates/dcim/manufacturer.html +24 -0
- nautobot/dcim/templates/dcim/module/base.html +97 -0
- nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
- nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
- nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
- nautobot/dcim/templates/dcim/module_destroy.html +5 -0
- nautobot/dcim/templates/dcim/module_frontports.html +53 -0
- nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
- nautobot/dcim/templates/dcim/module_list.html +20 -0
- nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
- nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
- nautobot/dcim/templates/dcim/module_powerports.html +53 -0
- nautobot/dcim/templates/dcim/module_rearports.html +53 -0
- nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
- nautobot/dcim/templates/dcim/module_update.html +71 -0
- nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
- nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
- nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +159 -0
- nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
- nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
- nautobot/dcim/tests/test_api.py +693 -208
- nautobot/dcim/tests/test_filters.py +843 -217
- nautobot/dcim/tests/test_models.py +1072 -8
- nautobot/dcim/tests/test_views.py +1510 -341
- nautobot/dcim/urls.py +17 -2
- nautobot/dcim/utils.py +2 -3
- nautobot/dcim/views.py +1106 -116
- nautobot/extras/__init__.py +0 -1
- nautobot/extras/api/serializers.py +115 -3
- nautobot/extras/api/urls.py +12 -0
- nautobot/extras/api/views.py +66 -0
- nautobot/extras/apps.py +2 -2
- nautobot/extras/choices.py +43 -0
- nautobot/extras/context_managers.py +13 -8
- nautobot/extras/datasources/git.py +2 -0
- nautobot/extras/factory.py +460 -9
- nautobot/extras/filters/__init__.py +174 -3
- nautobot/extras/filters/mixins.py +46 -43
- nautobot/extras/forms/base.py +24 -5
- nautobot/extras/forms/forms.py +227 -8
- nautobot/extras/forms/mixins.py +93 -0
- nautobot/extras/graphql/types.py +23 -10
- nautobot/extras/homepage.py +14 -1
- nautobot/extras/management/__init__.py +1 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
- nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
- nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
- nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
- nautobot/extras/migrations/0111_metadata.py +162 -0
- nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
- nautobot/extras/migrations/0113_saved_views.py +77 -0
- nautobot/extras/models/__init__.py +15 -1
- nautobot/extras/models/change_logging.py +3 -3
- nautobot/extras/models/contacts.py +4 -0
- nautobot/extras/models/customfields.py +18 -3
- nautobot/extras/models/groups.py +389 -225
- nautobot/extras/models/jobs.py +6 -3
- nautobot/extras/models/metadata.py +441 -0
- nautobot/extras/models/mixins.py +72 -62
- nautobot/extras/models/models.py +118 -9
- nautobot/extras/models/relationships.py +9 -2
- nautobot/extras/models/tags.py +13 -2
- nautobot/extras/navigation.py +57 -0
- nautobot/extras/plugins/__init__.py +3 -1
- nautobot/extras/querysets.py +30 -66
- nautobot/extras/signals.py +95 -100
- nautobot/extras/tables.py +165 -12
- nautobot/extras/templates/extras/dynamicgroup.html +44 -15
- nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
- nautobot/extras/templates/extras/job.html +1 -1
- nautobot/extras/templates/extras/jobresult.html +61 -74
- nautobot/extras/templates/extras/metadatatype_create.html +89 -0
- nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
- nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
- nautobot/extras/templates/extras/objectchange_list.html +0 -12
- nautobot/extras/templates/extras/plugins_list.html +1 -3
- nautobot/extras/templates/extras/role_retrieve.html +48 -0
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
- nautobot/extras/tests/integration/test_customfields.py +1 -0
- nautobot/extras/tests/test_api.py +509 -23
- nautobot/extras/tests/test_changelog.py +20 -9
- nautobot/extras/tests/test_context_managers.py +22 -15
- nautobot/extras/tests/test_datasources.py +13 -1
- nautobot/extras/tests/test_dynamicgroups.py +201 -171
- nautobot/extras/tests/test_filters.py +211 -12
- nautobot/extras/tests/test_jobs.py +6 -6
- nautobot/extras/tests/test_models.py +501 -4
- nautobot/extras/tests/test_relationships.py +1 -0
- nautobot/extras/tests/test_views.py +565 -8
- nautobot/extras/tests/test_webhooks.py +1 -1
- nautobot/extras/urls.py +5 -0
- nautobot/extras/utils.py +51 -11
- nautobot/extras/views.py +542 -76
- nautobot/ipam/__init__.py +0 -1
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/factory.py +17 -19
- nautobot/ipam/filters.py +13 -0
- nautobot/ipam/forms.py +8 -4
- nautobot/ipam/graphql/types.py +2 -2
- nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
- nautobot/ipam/models.py +11 -8
- nautobot/ipam/querysets.py +1 -1
- nautobot/ipam/signals.py +4 -2
- nautobot/ipam/tables.py +5 -0
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/prefix.html +1 -0
- nautobot/ipam/tests/test_api.py +37 -18
- nautobot/ipam/tests/test_filters.py +26 -2
- nautobot/ipam/tests/test_models.py +6 -0
- nautobot/ipam/tests/test_querysets.py +1 -1
- nautobot/ipam/tests/test_views.py +3 -2
- nautobot/ipam/urls.py +2 -2
- nautobot/ipam/views.py +18 -26
- nautobot/project-static/css/base.css +20 -0
- nautobot/project-static/css/dark.css +11 -0
- nautobot/project-static/docs/404.html +892 -88
- nautobot/project-static/docs/apps/index.html +892 -88
- nautobot/project-static/docs/apps/nautobot-apps.html +892 -88
- nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +919 -120
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +904 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1618 -903
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +935 -144
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +977 -188
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +901 -99
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +897 -93
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +991 -193
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +974 -131
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1078 -272
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1242 -334
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1727 -875
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1164 -381
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2088 -1374
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2246 -1422
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +912 -111
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +963 -163
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1010 -223
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1913 -1277
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1846 -1102
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +904 -101
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2331 -1699
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1802 -1024
- nautobot/project-static/docs/development/apps/api/configuration-view.html +892 -88
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/global-search.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/graphql.html +892 -88
- nautobot/project-static/docs/development/apps/api/models/index.html +942 -90
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +892 -88
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +892 -88
- nautobot/project-static/docs/development/apps/api/prometheus.html +892 -88
- nautobot/project-static/docs/development/apps/api/setup.html +892 -88
- nautobot/project-static/docs/development/apps/api/testing.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +892 -88
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/base-template.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/index.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/notes.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +892 -88
- nautobot/project-static/docs/development/apps/api/views/urls.html +892 -88
- nautobot/project-static/docs/development/apps/index.html +892 -88
- nautobot/project-static/docs/development/apps/migration/code-updates.html +892 -88
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +892 -88
- nautobot/project-static/docs/development/apps/migration/from-v1.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +892 -88
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +892 -88
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +892 -88
- nautobot/project-static/docs/development/core/application-registry.html +892 -88
- nautobot/project-static/docs/development/core/best-practices.html +893 -88
- nautobot/project-static/docs/development/core/bootstrap-ui.html +892 -88
- nautobot/project-static/docs/development/core/caching.html +892 -88
- nautobot/project-static/docs/development/core/controllers.html +892 -88
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +892 -88
- nautobot/project-static/docs/development/core/generic-views.html +892 -88
- nautobot/project-static/docs/development/core/getting-started.html +892 -88
- nautobot/project-static/docs/development/core/homepage.html +892 -88
- nautobot/project-static/docs/development/core/index.html +892 -88
- nautobot/project-static/docs/development/core/model-checklist.html +901 -89
- nautobot/project-static/docs/development/core/model-features.html +892 -88
- nautobot/project-static/docs/development/core/natural-keys.html +892 -88
- nautobot/project-static/docs/development/core/navigation-menu.html +892 -88
- nautobot/project-static/docs/development/core/release-checklist.html +895 -91
- nautobot/project-static/docs/development/core/role-internals.html +892 -88
- nautobot/project-static/docs/development/core/settings.html +892 -88
- nautobot/project-static/docs/development/core/style-guide.html +893 -89
- nautobot/project-static/docs/development/core/templates.html +904 -89
- nautobot/project-static/docs/development/core/testing.html +892 -88
- nautobot/project-static/docs/development/core/user-preferences.html +892 -88
- nautobot/project-static/docs/development/index.html +892 -88
- nautobot/project-static/docs/development/jobs/index.html +893 -89
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +892 -88
- nautobot/project-static/docs/index.html +892 -88
- nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
- nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
- nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
- nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
- nautobot/project-static/docs/models/dcim/module.html +15 -0
- nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
- nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
- nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
- nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
- nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
- nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
- nautobot/project-static/docs/models/extras/role.html +15 -0
- nautobot/project-static/docs/models/extras/savedview.html +15 -0
- nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
- nautobot/project-static/docs/models/extras/status.html +15 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +900 -89
- nautobot/project-static/docs/overview/design_philosophy.html +892 -88
- nautobot/project-static/docs/release-notes/index.html +1129 -92
- nautobot/project-static/docs/release-notes/version-1.0.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.1.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.2.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.3.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.4.html +892 -88
- nautobot/project-static/docs/release-notes/version-1.5.html +893 -89
- nautobot/project-static/docs/release-notes/version-1.6.html +893 -89
- nautobot/project-static/docs/release-notes/version-2.0.html +892 -88
- nautobot/project-static/docs/release-notes/version-2.1.html +892 -88
- nautobot/project-static/docs/release-notes/version-2.2.html +895 -91
- nautobot/project-static/docs/release-notes/version-2.3.html +9954 -0
- nautobot/project-static/docs/requirements.txt +5 -5
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +331 -256
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +992 -174
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +892 -88
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +896 -88
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +892 -88
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +944 -153
- nautobot/project-static/docs/user-guide/administration/installation/index.html +901 -93
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +934 -122
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +954 -157
- nautobot/project-static/docs/user-guide/administration/installation/services.html +913 -112
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +908 -99
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +892 -88
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +892 -88
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +892 -88
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +892 -88
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +893 -89
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +892 -88
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +893 -89
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +896 -88
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +895 -91
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +918 -100
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +923 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +913 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +920 -116
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +921 -117
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +914 -105
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +926 -108
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +936 -118
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +928 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +937 -119
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +928 -110
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +921 -117
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +923 -115
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +916 -112
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +940 -83
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +943 -86
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +921 -103
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +929 -125
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +918 -114
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +922 -104
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +924 -106
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +906 -102
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +936 -88
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +897 -89
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +897 -89
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +901 -96
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +892 -88
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +897 -89
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +892 -88
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +892 -88
- nautobot/project-static/docs/user-guide/index.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1258 -785
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +896 -88
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +895 -91
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +950 -121
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +892 -88
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +892 -88
- nautobot/project-static/js/forms.js +71 -0
- nautobot/project-static/js/table_sorting_indicator.js +46 -0
- nautobot/project-static/js/tableconfig.js +6 -1
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
- nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
- nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
- nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
- nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
- nautobot/tenancy/__init__.py +0 -1
- nautobot/tenancy/apps.py +1 -0
- nautobot/tenancy/factory.py +3 -2
- nautobot/tenancy/filters/__init__.py +1 -0
- nautobot/tenancy/forms.py +1 -1
- nautobot/tenancy/templates/tenancy/tenant.html +24 -20
- nautobot/tenancy/views.py +11 -10
- nautobot/users/__init__.py +0 -1
- nautobot/users/api/serializers.py +1 -1
- nautobot/users/api/views.py +4 -2
- nautobot/users/apps.py +3 -2
- nautobot/users/factory.py +3 -3
- nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
- nautobot/users/models.py +12 -0
- nautobot/users/tests/test_filters.py +6 -3
- nautobot/users/urls.py +8 -0
- nautobot/virtualization/__init__.py +0 -1
- nautobot/virtualization/apps.py +1 -0
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/forms.py +11 -3
- nautobot/virtualization/graphql/types.py +2 -2
- nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
- nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
- nautobot/virtualization/models.py +0 -2
- nautobot/virtualization/tables.py +10 -3
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
- nautobot/virtualization/tests/test_api.py +9 -4
- nautobot/virtualization/tests/test_filters.py +22 -0
- nautobot/virtualization/tests/test_models.py +7 -3
- nautobot/virtualization/tests/test_views.py +19 -3
- nautobot/virtualization/urls.py +2 -2
- nautobot/virtualization/views.py +10 -32
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/METADATA +20 -18
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/RECORD +677 -557
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
- nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
- nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
- nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
- nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
- nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
- nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
- nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
- nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
- nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
- nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
- nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
- nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
- /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
- {nautobot-2.2.9.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
nautobot/extras/models/jobs.py
CHANGED
|
@@ -43,7 +43,7 @@ from nautobot.extras.constants import (
|
|
|
43
43
|
)
|
|
44
44
|
from nautobot.extras.managers import JobResultManager, ScheduledJobsManager
|
|
45
45
|
from nautobot.extras.models import ChangeLoggedModel, GitRepository
|
|
46
|
-
from nautobot.extras.models.mixins import NotesMixin
|
|
46
|
+
from nautobot.extras.models.mixins import ContactMixin, DynamicGroupsModelMixin, NotesMixin
|
|
47
47
|
from nautobot.extras.querysets import JobQuerySet, ScheduledJobExtendedQuerySet
|
|
48
48
|
from nautobot.extras.utils import (
|
|
49
49
|
ChangeLoggedModelsQuery,
|
|
@@ -456,6 +456,8 @@ class JobLogEntry(BaseModel):
|
|
|
456
456
|
log_object = models.CharField(max_length=JOB_LOG_MAX_LOG_OBJECT_LENGTH, blank=True, default="")
|
|
457
457
|
absolute_url = models.CharField(max_length=JOB_LOG_MAX_ABSOLUTE_URL_LENGTH, blank=True, default="")
|
|
458
458
|
|
|
459
|
+
is_metadata_associable_model = False
|
|
460
|
+
|
|
459
461
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
460
462
|
|
|
461
463
|
def __str__(self):
|
|
@@ -793,9 +795,10 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
793
795
|
|
|
794
796
|
|
|
795
797
|
@extras_features("graphql")
|
|
796
|
-
class JobButton(
|
|
798
|
+
class JobButton(ContactMixin, ChangeLoggedModel, DynamicGroupsModelMixin, NotesMixin, BaseModel):
|
|
797
799
|
"""
|
|
798
|
-
A predefined button that includes all
|
|
800
|
+
A predefined button that includes all information to run a Nautobot Job based on a single object as a source.
|
|
801
|
+
|
|
799
802
|
The button text field accepts Jinja2 template code to be rendered with an object as context.
|
|
800
803
|
"""
|
|
801
804
|
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
from datetime import date, datetime, timezone
|
|
2
|
+
|
|
3
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.core.cache import cache
|
|
6
|
+
from django.core.exceptions import ValidationError
|
|
7
|
+
from django.db import models
|
|
8
|
+
|
|
9
|
+
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
10
|
+
from nautobot.core.models import BaseManager, BaseModel
|
|
11
|
+
from nautobot.core.models.fields import JSONArrayField
|
|
12
|
+
from nautobot.core.models.generics import PrimaryModel
|
|
13
|
+
from nautobot.core.models.querysets import RestrictedQuerySet
|
|
14
|
+
from nautobot.core.settings_funcs import is_truthy
|
|
15
|
+
from nautobot.extras.choices import MetadataTypeDataTypeChoices
|
|
16
|
+
from nautobot.extras.models.change_logging import ChangeLoggedModel
|
|
17
|
+
from nautobot.extras.models.contacts import Contact, Team
|
|
18
|
+
from nautobot.extras.utils import extras_features, FeatureQuery
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MetadataTypeManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
22
|
+
use_in_migrations = True
|
|
23
|
+
|
|
24
|
+
def get_for_model(self, model):
|
|
25
|
+
"""Return all MetadataTypes assigned to the given model."""
|
|
26
|
+
concrete_model = model._meta.concrete_model
|
|
27
|
+
cache_key = f"{self.get_for_model.cache_key_prefix}.{concrete_model._meta.label_lower}"
|
|
28
|
+
queryset = cache.get(cache_key)
|
|
29
|
+
if queryset is None:
|
|
30
|
+
content_type = ContentType.objects.get_for_model(concrete_model)
|
|
31
|
+
queryset = self.get_queryset().filter(content_types=content_type)
|
|
32
|
+
cache.set(cache_key, queryset)
|
|
33
|
+
return queryset
|
|
34
|
+
|
|
35
|
+
get_for_model.cache_key_prefix = "nautobot.extras.metadatatype.get_for_model"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@extras_features(
|
|
39
|
+
"custom_links",
|
|
40
|
+
"custom_validators",
|
|
41
|
+
"export_templates",
|
|
42
|
+
"graphql",
|
|
43
|
+
"webhooks",
|
|
44
|
+
)
|
|
45
|
+
class MetadataType(PrimaryModel):
|
|
46
|
+
content_types = models.ManyToManyField(
|
|
47
|
+
to=ContentType,
|
|
48
|
+
related_name="metadata_types",
|
|
49
|
+
limit_choices_to=FeatureQuery("metadata"),
|
|
50
|
+
help_text="The object type(s) to which Metadata of this type can be applied.",
|
|
51
|
+
)
|
|
52
|
+
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
|
|
53
|
+
description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
|
|
54
|
+
data_type = models.CharField(
|
|
55
|
+
max_length=50,
|
|
56
|
+
choices=MetadataTypeDataTypeChoices,
|
|
57
|
+
help_text="The type of data allowed for any Metadata of this type.",
|
|
58
|
+
)
|
|
59
|
+
# TODO: validation_minimum, validation_maximum, validation_regex?
|
|
60
|
+
# TODO: weight, grouping, advanced_ui?
|
|
61
|
+
|
|
62
|
+
objects = MetadataTypeManager()
|
|
63
|
+
clone_fields = ["data_type"]
|
|
64
|
+
documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
|
|
65
|
+
|
|
66
|
+
class Meta:
|
|
67
|
+
ordering = ["name"]
|
|
68
|
+
|
|
69
|
+
def __str__(self):
|
|
70
|
+
return self.name
|
|
71
|
+
|
|
72
|
+
def clean(self):
|
|
73
|
+
super().clean()
|
|
74
|
+
|
|
75
|
+
if self.present_in_database:
|
|
76
|
+
# Check immutable fields
|
|
77
|
+
database_object = self.__class__.objects.get(pk=self.pk)
|
|
78
|
+
if self.data_type != database_object.data_type:
|
|
79
|
+
raise ValidationError({"data_type": "Type cannot be changed once created"})
|
|
80
|
+
|
|
81
|
+
# Choices can be set only on selection fields
|
|
82
|
+
if self.choices.exists() and self.data_type not in (
|
|
83
|
+
MetadataTypeDataTypeChoices.TYPE_SELECT,
|
|
84
|
+
MetadataTypeDataTypeChoices.TYPE_MULTISELECT,
|
|
85
|
+
):
|
|
86
|
+
raise ValidationError("Choices may be set only for select/multi-select data_type.")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@extras_features(
|
|
90
|
+
"custom_validators",
|
|
91
|
+
"graphql",
|
|
92
|
+
"webhooks",
|
|
93
|
+
)
|
|
94
|
+
class MetadataChoice(ChangeLoggedModel, BaseModel):
|
|
95
|
+
"""Store the possible set of values for a select/multi-select metadata type."""
|
|
96
|
+
|
|
97
|
+
metadata_type = models.ForeignKey(
|
|
98
|
+
to=MetadataType,
|
|
99
|
+
on_delete=models.CASCADE,
|
|
100
|
+
related_name="choices",
|
|
101
|
+
limit_choices_to=models.Q(
|
|
102
|
+
data_type__in=[MetadataTypeDataTypeChoices.TYPE_SELECT, MetadataTypeDataTypeChoices.TYPE_MULTISELECT]
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
value = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
|
|
106
|
+
weight = models.PositiveSmallIntegerField(default=100, help_text="Higher weights appear later in the list")
|
|
107
|
+
is_metadata_associable_model = False
|
|
108
|
+
|
|
109
|
+
documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
|
|
110
|
+
|
|
111
|
+
class Meta:
|
|
112
|
+
ordering = ["metadata_type", "weight", "value"]
|
|
113
|
+
unique_together = ["metadata_type", "value"]
|
|
114
|
+
|
|
115
|
+
def __str__(self):
|
|
116
|
+
return self.value
|
|
117
|
+
|
|
118
|
+
def clean(self):
|
|
119
|
+
super().clean()
|
|
120
|
+
|
|
121
|
+
if self.present_in_database:
|
|
122
|
+
# Check immutable fields
|
|
123
|
+
database_object = self.__class__.objects.get(pk=self.pk)
|
|
124
|
+
if self.metadata_type != database_object.metadata_type:
|
|
125
|
+
raise ValidationError({"metadata_type": "Cannot be changed once created"})
|
|
126
|
+
|
|
127
|
+
if self.metadata_type.data_type not in (
|
|
128
|
+
MetadataTypeDataTypeChoices.TYPE_SELECT,
|
|
129
|
+
MetadataTypeDataTypeChoices.TYPE_MULTISELECT,
|
|
130
|
+
):
|
|
131
|
+
raise ValidationError(
|
|
132
|
+
{"metadata_type": "Metadata choices can only be assigned to select/multiselect data_type."}
|
|
133
|
+
)
|
|
134
|
+
# TODO: enforce validation_minimum, validation_maximum, validation_regex like CustomFieldChoice does?
|
|
135
|
+
|
|
136
|
+
def save(self, *args, **kwargs):
|
|
137
|
+
super().save(*args, **kwargs)
|
|
138
|
+
# TODO: on change of value, enqueue background task to go through all existing metadata and update them,
|
|
139
|
+
# like CustomFieldChoice does?
|
|
140
|
+
# TODO: Alternately, should we let Metadata just have a FK directly to its corresponding MetadataTypeChoice?
|
|
141
|
+
|
|
142
|
+
def delete(self, *args, **kwargs):
|
|
143
|
+
# TODO: should we behave like CustomFieldChoice and raise a ProtectedError if this choice is in use anywhere?
|
|
144
|
+
super().delete(*args, **kwargs)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ObjectMetadataManager(BaseManager.from_queryset(RestrictedQuerySet)):
|
|
148
|
+
use_in_migrations = True
|
|
149
|
+
|
|
150
|
+
def get_for_object(self, instance):
|
|
151
|
+
"""Return all ObjectMetadatas assigned to the given instance."""
|
|
152
|
+
assigned_object_type = ContentType.objects.get_for_model(instance)
|
|
153
|
+
assigned_object_id = instance.pk
|
|
154
|
+
queryset = self.get_queryset().filter(
|
|
155
|
+
assigned_object_type=assigned_object_type, assigned_object_id=assigned_object_id
|
|
156
|
+
)
|
|
157
|
+
return queryset
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@extras_features(
|
|
161
|
+
"custom_validators",
|
|
162
|
+
"graphql",
|
|
163
|
+
"webhooks",
|
|
164
|
+
)
|
|
165
|
+
class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
166
|
+
metadata_type = models.ForeignKey(
|
|
167
|
+
to=MetadataType,
|
|
168
|
+
on_delete=models.PROTECT,
|
|
169
|
+
related_name="object_metadata",
|
|
170
|
+
)
|
|
171
|
+
contact = models.ForeignKey(
|
|
172
|
+
to=Contact,
|
|
173
|
+
on_delete=models.PROTECT,
|
|
174
|
+
related_name="object_metadata",
|
|
175
|
+
blank=True,
|
|
176
|
+
null=True,
|
|
177
|
+
)
|
|
178
|
+
team = models.ForeignKey(
|
|
179
|
+
to=Team,
|
|
180
|
+
on_delete=models.PROTECT,
|
|
181
|
+
related_name="object_metadata",
|
|
182
|
+
blank=True,
|
|
183
|
+
null=True,
|
|
184
|
+
)
|
|
185
|
+
scoped_fields = JSONArrayField(
|
|
186
|
+
base_field=models.CharField(
|
|
187
|
+
max_length=CHARFIELD_MAX_LENGTH,
|
|
188
|
+
),
|
|
189
|
+
blank=True,
|
|
190
|
+
help_text="List of scoped fields, only direct fields on the model",
|
|
191
|
+
)
|
|
192
|
+
_value = models.JSONField(
|
|
193
|
+
blank=True,
|
|
194
|
+
null=True,
|
|
195
|
+
help_text="Relevant data value to an object field or a set of object fields",
|
|
196
|
+
)
|
|
197
|
+
assigned_object_type = models.ForeignKey(to=ContentType, on_delete=models.SET_NULL, null=True, related_name="+")
|
|
198
|
+
assigned_object_id = models.UUIDField(db_index=True)
|
|
199
|
+
assigned_object = GenericForeignKey(ct_field="assigned_object_type", fk_field="assigned_object_id")
|
|
200
|
+
|
|
201
|
+
objects = ObjectMetadataManager()
|
|
202
|
+
natural_key_field_names = ["pk"]
|
|
203
|
+
documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
|
|
204
|
+
|
|
205
|
+
class Meta:
|
|
206
|
+
ordering = ["metadata_type"]
|
|
207
|
+
verbose_name_plural = "object metadata"
|
|
208
|
+
|
|
209
|
+
indexes = [
|
|
210
|
+
models.Index(
|
|
211
|
+
name="assigned_object",
|
|
212
|
+
fields=["assigned_object_type", "assigned_object_id"],
|
|
213
|
+
),
|
|
214
|
+
models.Index(
|
|
215
|
+
name="assigned_object_contact",
|
|
216
|
+
fields=["assigned_object_type", "assigned_object_id", "contact"],
|
|
217
|
+
),
|
|
218
|
+
models.Index(
|
|
219
|
+
name="assigned_object_team",
|
|
220
|
+
fields=["assigned_object_type", "assigned_object_id", "team"],
|
|
221
|
+
),
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def value(self):
|
|
226
|
+
if self.metadata_type.data_type == MetadataTypeDataTypeChoices.TYPE_CONTACT_TEAM:
|
|
227
|
+
return None
|
|
228
|
+
else:
|
|
229
|
+
return self._value
|
|
230
|
+
|
|
231
|
+
@value.setter
|
|
232
|
+
def value(self, v):
|
|
233
|
+
if self.metadata_type.data_type == MetadataTypeDataTypeChoices.TYPE_CONTACT_TEAM:
|
|
234
|
+
if v is not None:
|
|
235
|
+
raise ValidationError(
|
|
236
|
+
f"{v} is an invalid value for metadata type data type {self.metadata_type.data_type}"
|
|
237
|
+
)
|
|
238
|
+
else:
|
|
239
|
+
self._value = v
|
|
240
|
+
self.clean()
|
|
241
|
+
|
|
242
|
+
def __str__(self):
|
|
243
|
+
if self.metadata_type.data_type == MetadataTypeDataTypeChoices.TYPE_CONTACT_TEAM:
|
|
244
|
+
return f"{self.metadata_type} - {self.assigned_object} - {self.contact or self.team}"
|
|
245
|
+
return f"{self.metadata_type} - {self.assigned_object} - {self.value}"
|
|
246
|
+
|
|
247
|
+
def validated_save(self, *args, **kwargs):
|
|
248
|
+
# call clean() first so that the data type-conversion is done first
|
|
249
|
+
# so that clean_fields() would not raise any errors
|
|
250
|
+
self.clean()
|
|
251
|
+
return super().validated_save(*args, **kwargs)
|
|
252
|
+
|
|
253
|
+
def clean(self):
|
|
254
|
+
"""
|
|
255
|
+
Validate a value according to the field's type validation rules.
|
|
256
|
+
|
|
257
|
+
Returns the value, possibly cleaned up
|
|
258
|
+
"""
|
|
259
|
+
super().clean()
|
|
260
|
+
value = self.value
|
|
261
|
+
metadata_type_data_type = self.metadata_type.data_type
|
|
262
|
+
if self.present_in_database:
|
|
263
|
+
# Check immutable fields
|
|
264
|
+
database_object = self.__class__.objects.get(pk=self.pk)
|
|
265
|
+
if self.metadata_type != database_object.metadata_type:
|
|
266
|
+
raise ValidationError({"metadata_type": "Cannot be changed once created"})
|
|
267
|
+
# Validate the assigned_object_type is allowed in metadata_type's content_types
|
|
268
|
+
if not self.metadata_type.content_types.filter(pk=self.assigned_object_type.id).exists():
|
|
269
|
+
raise ValidationError(
|
|
270
|
+
f"Assigned Object Type {self.assigned_object_type} is not allowed by Metadata Type {self.metadata_type}"
|
|
271
|
+
)
|
|
272
|
+
# Check for MetadataTypeDataTypeChoices.TYPE_CONTACT_TEAM first
|
|
273
|
+
if metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_CONTACT_TEAM:
|
|
274
|
+
if value is not None:
|
|
275
|
+
raise ValidationError(f"A value cannot be specified for type {metadata_type_data_type}")
|
|
276
|
+
if self.contact is None and self.team is None:
|
|
277
|
+
raise ValidationError("Either a contact or a team must be specified")
|
|
278
|
+
if self.contact is not None and self.team is not None:
|
|
279
|
+
raise ValidationError("A contact and a team cannot be both specified at once")
|
|
280
|
+
elif value is None:
|
|
281
|
+
raise ValidationError(
|
|
282
|
+
f"value is a required field that cannot be empty for metadata type {metadata_type_data_type}."
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
# Validate text field
|
|
286
|
+
if metadata_type_data_type in (
|
|
287
|
+
MetadataTypeDataTypeChoices.TYPE_TEXT,
|
|
288
|
+
MetadataTypeDataTypeChoices.TYPE_URL,
|
|
289
|
+
MetadataTypeDataTypeChoices.TYPE_MARKDOWN,
|
|
290
|
+
):
|
|
291
|
+
if not isinstance(value, str):
|
|
292
|
+
raise ValidationError("Value must be a string")
|
|
293
|
+
# TODO uncomment this when MetaDataType validation_minimum, validation_maximum and validation_regex fields are implemented
|
|
294
|
+
# if self.metadata_type.validation_minimum is not None and len(value) < self.metadata_type.validation_minimum:
|
|
295
|
+
# raise ValidationError(f"Value must be at least {self.metadata_type.validation_minimum} characters in length")
|
|
296
|
+
# if self.metadata_type.validation_maximum is not None and len(value) > self.metadata_type.validation_maximum:
|
|
297
|
+
# raise ValidationError(f"Value must not exceed {self.metadata_type.validation_maximum} characters in length")
|
|
298
|
+
# if self.metadata_type.validation_regex and not re.search(self.metadata_type.validation_regex, value):
|
|
299
|
+
# raise ValidationError(f"Value must match regex '{self.metadata_type.validation_regex}'")
|
|
300
|
+
|
|
301
|
+
# Validate JSON
|
|
302
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_JSON:
|
|
303
|
+
pass
|
|
304
|
+
# TODO uncomment this when MetaDataType validation_minimum, validation_maximum and validation_regex fields are implemented
|
|
305
|
+
# if (
|
|
306
|
+
# self.metadata_type.validation_regex
|
|
307
|
+
# or self.metadata_type.validation_minimum is not None
|
|
308
|
+
# or self.metadata_type.validation_maximum is not None
|
|
309
|
+
# ):
|
|
310
|
+
# json_value = json.dumps(value)
|
|
311
|
+
# if (
|
|
312
|
+
# self.metadata_type.validation_minimum is not None
|
|
313
|
+
# and len(json_value) < self.metadata_type.validation_minimum
|
|
314
|
+
# ):
|
|
315
|
+
# raise ValidationError(
|
|
316
|
+
# f"Value must be at least {self.metadata_type.validation_minimum} characters in length"
|
|
317
|
+
# )
|
|
318
|
+
# if (
|
|
319
|
+
# self.metadata_type.validation_maximum is not None
|
|
320
|
+
# and len(json_value) > self.metadata_type.validation_maximum
|
|
321
|
+
# ):
|
|
322
|
+
# raise ValidationError(
|
|
323
|
+
# f"Value must not exceed {self.metadata_type.validation_maximum} characters in length"
|
|
324
|
+
# )
|
|
325
|
+
# if self.metadata_type.validation_regex and not re.search(
|
|
326
|
+
# self.metadata_type.validation_regex, json_value
|
|
327
|
+
# ):
|
|
328
|
+
# raise ValidationError(f"Value must match regex '{self.metadata_type.validation_regex}'")
|
|
329
|
+
|
|
330
|
+
# Validate integer
|
|
331
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_INTEGER:
|
|
332
|
+
try:
|
|
333
|
+
value = int(value)
|
|
334
|
+
except ValueError:
|
|
335
|
+
raise ValidationError("Value must be an integer.")
|
|
336
|
+
# TODO uncomment this when MetaDataType validation_minimum, validation_maximum and validation_regex fields are implemented
|
|
337
|
+
# if self.metadata_type.validation_minimum is not None and value < self.metadata_type.validation_minimum:
|
|
338
|
+
# raise ValidationError(f"Value must be at least {self.metadata_type.validation_minimum}")
|
|
339
|
+
# if self.metadata_type.validation_maximum is not None and value > self.metadata_type.validation_maximum:
|
|
340
|
+
# raise ValidationError(f"Value must not exceed {self.metadata_type.validation_maximum}")
|
|
341
|
+
# Validate integer
|
|
342
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_FLOAT:
|
|
343
|
+
try:
|
|
344
|
+
value = float(value)
|
|
345
|
+
except ValueError:
|
|
346
|
+
raise ValidationError("Value must be a float.")
|
|
347
|
+
# Validate boolean
|
|
348
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_BOOLEAN:
|
|
349
|
+
try:
|
|
350
|
+
value = is_truthy(value)
|
|
351
|
+
except ValueError as exc:
|
|
352
|
+
raise ValidationError("Value must be true or false.") from exc
|
|
353
|
+
|
|
354
|
+
# Validate date
|
|
355
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_DATE:
|
|
356
|
+
if not isinstance(value, date):
|
|
357
|
+
try:
|
|
358
|
+
datetime.strptime(value, "%Y-%m-%d")
|
|
359
|
+
except TypeError:
|
|
360
|
+
raise ValidationError("Value must be a date or str object.")
|
|
361
|
+
except ValueError:
|
|
362
|
+
raise ValidationError("Date values must be in the format YYYY-MM-DD.")
|
|
363
|
+
# Validate datetime
|
|
364
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_DATETIME:
|
|
365
|
+
if isinstance(value, datetime):
|
|
366
|
+
# check if datetime object has tzinfo
|
|
367
|
+
if value.tzinfo is None:
|
|
368
|
+
value = value.replace(tzinfo=timezone.utc) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
|
369
|
+
value = value.replace(microsecond=0).isoformat() # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
|
370
|
+
else:
|
|
371
|
+
acceptable_datetime_formats = [
|
|
372
|
+
"YYYY-MM-DDTHH:MM:SS",
|
|
373
|
+
"YYYY-MM-DDTHH:MM:SS(+,-)zzzz",
|
|
374
|
+
"YYYY-MM-DDTHH:MM:SS(+,-)zz:zz",
|
|
375
|
+
]
|
|
376
|
+
try:
|
|
377
|
+
datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z")
|
|
378
|
+
except ValueError:
|
|
379
|
+
try:
|
|
380
|
+
# No time zone info entered so assume UTC timezone
|
|
381
|
+
datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
|
|
382
|
+
value += "+0000"
|
|
383
|
+
except ValueError:
|
|
384
|
+
raise ValidationError(
|
|
385
|
+
f"Datetime values must be in the following formats {acceptable_datetime_formats}"
|
|
386
|
+
)
|
|
387
|
+
except TypeError:
|
|
388
|
+
raise ValidationError("Value must be a datetime or str object.")
|
|
389
|
+
# Validate selected choice
|
|
390
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_SELECT:
|
|
391
|
+
if value not in self.metadata_type.choices.values_list("value", flat=True):
|
|
392
|
+
raise ValidationError(
|
|
393
|
+
f"Invalid choice ({value}). Available choices are: {', '.join(self.metadata_type.choices.values_list('value', flat=True))}"
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_MULTISELECT:
|
|
397
|
+
if isinstance(value, str):
|
|
398
|
+
value = value.split(",")
|
|
399
|
+
if not set(value).issubset(self.metadata_type.choices.values_list("value", flat=True)):
|
|
400
|
+
raise ValidationError(
|
|
401
|
+
f"Invalid choice(s) ({value}). Available choices are: {', '.join(self.metadata_type.choices.values_list('value', flat=True))}"
|
|
402
|
+
)
|
|
403
|
+
self._value = value
|
|
404
|
+
|
|
405
|
+
# Check if there is any intersections of scoped_fields of ObjectMetadata instances in the database.
|
|
406
|
+
object_metadata_scoped_fields = (
|
|
407
|
+
ObjectMetadata.objects.filter(
|
|
408
|
+
metadata_type=self.metadata_type,
|
|
409
|
+
assigned_object_type=self.assigned_object_type,
|
|
410
|
+
assigned_object_id=self.assigned_object_id,
|
|
411
|
+
)
|
|
412
|
+
.exclude(pk=self.pk)
|
|
413
|
+
.values_list("scoped_fields", flat=True)
|
|
414
|
+
)
|
|
415
|
+
duplicate_scoped_fields_list = set([])
|
|
416
|
+
|
|
417
|
+
# TODO Make sure that scoped fields are all direct fields on the assigned_object_type model
|
|
418
|
+
|
|
419
|
+
for scoped_fields in object_metadata_scoped_fields:
|
|
420
|
+
duplicate_scoped_fields_list |= set(self.scoped_fields).intersection(scoped_fields)
|
|
421
|
+
# if self.scoped_fields or scoped_fields are [] which means all fields are scoped
|
|
422
|
+
# That means any fields in self.scoped_fields or scoped_fields are considered overlapped
|
|
423
|
+
if not self.scoped_fields:
|
|
424
|
+
raise ValidationError(
|
|
425
|
+
{
|
|
426
|
+
"scoped_fields": f"This Object Metadata {self} scoping all the fields for {self.assigned_object} has overlapped with the scoped fields of other Object Metadata instances."
|
|
427
|
+
}
|
|
428
|
+
)
|
|
429
|
+
if not scoped_fields:
|
|
430
|
+
raise ValidationError(
|
|
431
|
+
{
|
|
432
|
+
"scoped_fields": f"There are other Object Metadata instances of metadata type {self.metadata_type} scoping all the fields for {self.assigned_object}"
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
if duplicate_scoped_fields_list:
|
|
437
|
+
raise ValidationError(
|
|
438
|
+
{
|
|
439
|
+
"scoped_fields": f"There are other Object Metadata instances of metadata type {self.metadata_type} scoping the same fields {duplicate_scoped_fields_list} for {self.assigned_object}"
|
|
440
|
+
}
|
|
441
|
+
)
|
nautobot/extras/models/mixins.py
CHANGED
|
@@ -2,95 +2,79 @@
|
|
|
2
2
|
Class-modifying mixins that need to be standalone to avoid circular imports.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from django.contrib.contenttypes.fields import GenericRelation
|
|
6
|
+
from django.db import models
|
|
5
7
|
from django.urls import NoReverseMatch, reverse
|
|
6
8
|
|
|
9
|
+
from nautobot.core.utils.deprecation import method_deprecated_in_favor_of
|
|
7
10
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
class
|
|
11
|
-
"""
|
|
12
|
-
Adds properties to a model to facilitate reversing DynamicGroup membership:
|
|
13
|
+
class ContactMixin(models.Model):
|
|
14
|
+
"""Abstract mixin for enabling Contact/Team association to a given model class."""
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- `dynamic_groups_list` - A list of `DynamicGroup` objects this instance is a member of, performs one less database query than `dynamic_groups`.
|
|
17
|
-
- `dynamic_groups_list_cached` - A list of `DynamicGroup` objects this instance is a member of, uses cached member list if available. Performs no database queries in optimal conditions.
|
|
16
|
+
class Meta:
|
|
17
|
+
abstract = True
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
EX: `delattr(instance, "_dynamic_groups")`
|
|
21
|
-
"""
|
|
19
|
+
is_contact_associable_model = True
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
""
|
|
26
|
-
|
|
21
|
+
# Reverse relation so that deleting a ContactMixin automatically deletes any ContactAssociations related to it.
|
|
22
|
+
associated_contacts = GenericRelation(
|
|
23
|
+
"extras.ContactAssociation",
|
|
24
|
+
content_type_field="associated_object_type",
|
|
25
|
+
object_id_field="associated_object_id",
|
|
26
|
+
related_query_name="associated_contacts_%(app_label)s_%(class)s", # e.g. 'associated_contacts_dcim_device'
|
|
27
|
+
)
|
|
27
28
|
|
|
28
|
-
This will NOT use the cached member lists of the dynamic groups and will always query the database for each DynamicGroup.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
class DynamicGroupMixin:
|
|
31
|
+
"""
|
|
32
|
+
DEPRECATED - use DynamicGroupsModelMixin instead if you need to mark a model as supporting Dynamic Groups.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
This is necessary because DynamicGroupMixin was incorrectly not implemented as a subclass of models.Model,
|
|
35
|
+
and so it cannot properly implement Model behaviors like the `static_group_association_set` ReverseRelation.
|
|
36
|
+
However, adding this inheritance to DynamicGroupMixin itself would negatively impact existing migrations.
|
|
37
|
+
So unfortunately our best option is to deprecate this class and gradually convert core and app models alike
|
|
38
|
+
to the new DynamicGroupsModelMixin in its place.
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
Adds `dynamic_groups` property to a model to facilitate reversing (cached) DynamicGroup membership.
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"""
|
|
43
|
-
Return a queryset of `DynamicGroup` objects this instance is a member of.
|
|
42
|
+
If up-to-the-minute accuracy is necessary for your use case, it's up to you to call the
|
|
43
|
+
`DynamicGroup.update_cached_members()` API on any relevant DynamicGroups before accessing this property.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Other related properties added by this mixin should be considered obsolete.
|
|
46
|
+
"""
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
is_dynamic_group_associable_model = True
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
@property
|
|
51
|
+
def dynamic_groups(self):
|
|
52
|
+
"""
|
|
53
|
+
Return a queryset of (cached) `DynamicGroup` objects this instance is a member of.
|
|
50
54
|
"""
|
|
51
55
|
from nautobot.extras.models.groups import DynamicGroup
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
queryset = DynamicGroup.objects.get_for_object(self, use_cache=True)
|
|
55
|
-
self._dynamic_groups_cached = queryset
|
|
57
|
+
return DynamicGroup.objects.get_for_object(self)
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
@property
|
|
60
|
+
@method_deprecated_in_favor_of(dynamic_groups.fget)
|
|
61
|
+
def dynamic_groups_cached(self):
|
|
62
|
+
"""Deprecated - use `self.dynamic_groups` instead."""
|
|
63
|
+
return self.dynamic_groups
|
|
58
64
|
|
|
59
65
|
@property
|
|
66
|
+
@method_deprecated_in_favor_of(dynamic_groups.fget)
|
|
60
67
|
def dynamic_groups_list(self):
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
This will NOT use the cached member lists of the dynamic groups and will always query the database for each DynamicGroup.
|
|
65
|
-
|
|
66
|
-
This saves a final query to turn the list into a queryset.
|
|
67
|
-
"""
|
|
68
|
-
from nautobot.extras.models.groups import DynamicGroup
|
|
69
|
-
|
|
70
|
-
if not hasattr(self, "_dynamic_groups_list"):
|
|
71
|
-
dg_list = DynamicGroup.objects.get_list_for_object(self)
|
|
72
|
-
self._dynamic_groups_list = dg_list
|
|
73
|
-
|
|
74
|
-
return self._dynamic_groups_list
|
|
68
|
+
"""Deprecated - use `list(self.dynamic_groups)` instead."""
|
|
69
|
+
return list(self.dynamic_groups)
|
|
75
70
|
|
|
76
71
|
@property
|
|
72
|
+
@method_deprecated_in_favor_of(dynamic_groups.fget)
|
|
77
73
|
def dynamic_groups_list_cached(self):
|
|
78
|
-
"""
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
This will use the cached member lists of the dynamic groups if available.
|
|
82
|
-
|
|
83
|
-
In optimal conditions this will incur no database queries.
|
|
84
|
-
"""
|
|
85
|
-
|
|
86
|
-
from nautobot.extras.models.groups import DynamicGroup
|
|
87
|
-
|
|
88
|
-
if not hasattr(self, "_dynamic_groups_list_cached"):
|
|
89
|
-
dg_list = DynamicGroup.objects.get_list_for_object(self, use_cache=True)
|
|
90
|
-
self._dynamic_groups_list_cached = dg_list
|
|
91
|
-
|
|
92
|
-
return self._dynamic_groups_list_cached
|
|
74
|
+
"""Deprecated - use `list(self.dynamic_groups)` instead."""
|
|
75
|
+
return self.dynamic_groups_list
|
|
93
76
|
|
|
77
|
+
# TODO may be able to remove this entirely???
|
|
94
78
|
def get_dynamic_groups_url(self):
|
|
95
79
|
"""Return the dynamic groups URL for a given instance."""
|
|
96
80
|
route = get_route_for_model(self, "dynamicgroups")
|
|
@@ -109,6 +93,23 @@ class DynamicGroupMixin:
|
|
|
109
93
|
return None
|
|
110
94
|
|
|
111
95
|
|
|
96
|
+
class DynamicGroupsModelMixin(DynamicGroupMixin, models.Model):
|
|
97
|
+
"""
|
|
98
|
+
Add this to models to make them fully support Dynamic Groups.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
class Meta:
|
|
102
|
+
abstract = True
|
|
103
|
+
|
|
104
|
+
# Reverse relation so that deleting a DynamicGroupMixin automatically deletes any related StaticGroupAssociations
|
|
105
|
+
static_group_association_set = GenericRelation( # not "static_group_associations" as that'd collide on DynamicGroup
|
|
106
|
+
"extras.StaticGroupAssociation",
|
|
107
|
+
content_type_field="associated_object_type",
|
|
108
|
+
object_id_field="associated_object_id",
|
|
109
|
+
related_query_name="static_group_association_set_%(app_label)s_%(class)s",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
112
113
|
class NotesMixin:
|
|
113
114
|
"""
|
|
114
115
|
Adds a `notes` property that returns a queryset of `Notes` membership.
|
|
@@ -141,3 +142,12 @@ class NotesMixin:
|
|
|
141
142
|
continue
|
|
142
143
|
|
|
143
144
|
return None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SavedViewMixin(models.Model):
|
|
148
|
+
"""Abstract mixin for enabling Saved View functionality to a given model class."""
|
|
149
|
+
|
|
150
|
+
class Meta:
|
|
151
|
+
abstract = True
|
|
152
|
+
|
|
153
|
+
is_saved_view_model = True
|