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/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
|
|
|
@@ -1074,6 +1077,87 @@ class ScheduledJob(BaseModel):
|
|
|
1074
1077
|
day_of_week=day_of_week,
|
|
1075
1078
|
)
|
|
1076
1079
|
|
|
1080
|
+
@classmethod
|
|
1081
|
+
def create_schedule(
|
|
1082
|
+
cls,
|
|
1083
|
+
job_model,
|
|
1084
|
+
user,
|
|
1085
|
+
name=None,
|
|
1086
|
+
start_time=None,
|
|
1087
|
+
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1088
|
+
crontab="",
|
|
1089
|
+
profile=False,
|
|
1090
|
+
approval_required=False,
|
|
1091
|
+
task_queue=None,
|
|
1092
|
+
**job_kwargs,
|
|
1093
|
+
):
|
|
1094
|
+
"""
|
|
1095
|
+
Schedule a job with the specified parameters.
|
|
1096
|
+
|
|
1097
|
+
This method creates a schedule for a job to be executed at a specific time
|
|
1098
|
+
or interval. It handles immediate execution, custom start times, and
|
|
1099
|
+
crontab-based scheduling.
|
|
1100
|
+
|
|
1101
|
+
Parameters:
|
|
1102
|
+
job_model (JobModel): The job model instance.
|
|
1103
|
+
user (User): The user who is scheduling the job.
|
|
1104
|
+
name (str, optional): The name of the scheduled job. Defaults to None.
|
|
1105
|
+
start_time (datetime, optional): The start time for the job. Defaults to None.
|
|
1106
|
+
interval (JobExecutionType, optional): The interval type for the job execution.
|
|
1107
|
+
Defaults to JobExecutionType.TYPE_IMMEDIATELY.
|
|
1108
|
+
crontab (str, optional): The crontab string for the schedule. Defaults to "".
|
|
1109
|
+
profile (bool, optional): Flag indicating whether to profile the job. Defaults to False.
|
|
1110
|
+
approval_required (bool, optional): Flag indicating if approval is required. Defaults to False.
|
|
1111
|
+
task_queue (str, optional): The task queue for the job. Defaults to None, which will use the configured default celery queue.
|
|
1112
|
+
**job_kwargs: Additional keyword arguments to pass to the job.
|
|
1113
|
+
|
|
1114
|
+
Returns:
|
|
1115
|
+
ScheduledJob instance
|
|
1116
|
+
"""
|
|
1117
|
+
|
|
1118
|
+
if interval == JobExecutionType.TYPE_IMMEDIATELY:
|
|
1119
|
+
start_time = timezone.now()
|
|
1120
|
+
name = name or f"{job_model.name} - {start_time}"
|
|
1121
|
+
elif interval == JobExecutionType.TYPE_CUSTOM:
|
|
1122
|
+
if start_time is None:
|
|
1123
|
+
# "start_time" is checked against models.ScheduledJob.earliest_possible_time()
|
|
1124
|
+
# which returns timezone.now() + timedelta(seconds=15)
|
|
1125
|
+
start_time = timezone.now() + timedelta(seconds=20)
|
|
1126
|
+
|
|
1127
|
+
celery_kwargs = {
|
|
1128
|
+
"nautobot_job_profile": profile,
|
|
1129
|
+
"queue": task_queue,
|
|
1130
|
+
}
|
|
1131
|
+
if job_model.soft_time_limit > 0:
|
|
1132
|
+
celery_kwargs["soft_time_limit"] = job_model.soft_time_limit
|
|
1133
|
+
if job_model.time_limit > 0:
|
|
1134
|
+
celery_kwargs["time_limit"] = job_model.time_limit
|
|
1135
|
+
|
|
1136
|
+
# 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
|
|
1137
|
+
#
|
|
1138
|
+
# We pass in task and job_model here partly for forward/backward compatibility logic, and
|
|
1139
|
+
# part fallback safety. It's mildly useful to store both the task module/class name and the JobModel
|
|
1140
|
+
# FK on the ScheduledJob, as in the case where the JobModel gets deleted (and the FK becomes
|
|
1141
|
+
# null) you still have a bit of context on the ScheduledJob as to what it was originally
|
|
1142
|
+
# scheduled for.
|
|
1143
|
+
scheduled_job = cls(
|
|
1144
|
+
name=name,
|
|
1145
|
+
task=job_model.class_path,
|
|
1146
|
+
job_model=job_model,
|
|
1147
|
+
start_time=start_time,
|
|
1148
|
+
description=f"Nautobot job {name} scheduled by {user} for {start_time}",
|
|
1149
|
+
kwargs=job_kwargs,
|
|
1150
|
+
celery_kwargs=celery_kwargs,
|
|
1151
|
+
interval=interval,
|
|
1152
|
+
one_off=(interval == JobExecutionType.TYPE_FUTURE),
|
|
1153
|
+
user=user,
|
|
1154
|
+
approval_required=approval_required,
|
|
1155
|
+
crontab=crontab,
|
|
1156
|
+
queue=task_queue,
|
|
1157
|
+
)
|
|
1158
|
+
scheduled_job.validated_save()
|
|
1159
|
+
return scheduled_job
|
|
1160
|
+
|
|
1077
1161
|
def to_cron(self):
|
|
1078
1162
|
t = self.start_time
|
|
1079
1163
|
if self.interval == JobExecutionType.TYPE_HOURLY:
|
|
@@ -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
|
+
)
|