nautobot 2.0.0a3__py3-none-any.whl → 2.0.0b1__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.
- nautobot/apps/api.py +6 -8
- nautobot/apps/forms.py +0 -2
- nautobot/apps/ui.py +0 -8
- nautobot/circuits/api/serializers.py +9 -117
- nautobot/circuits/api/urls.py +1 -1
- nautobot/circuits/api/views.py +0 -1
- nautobot/circuits/forms.py +0 -65
- nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
- nautobot/circuits/migrations/0016_tagsfield.py +34 -0
- nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
- nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
- nautobot/circuits/models.py +3 -87
- nautobot/circuits/navigation.py +14 -69
- nautobot/circuits/signals.py +0 -2
- nautobot/circuits/tables.py +39 -1
- nautobot/circuits/tests/integration/test_relationships.py +9 -9
- nautobot/circuits/tests/test_api.py +4 -8
- nautobot/circuits/tests/test_filters.py +10 -4
- nautobot/circuits/tests/test_models.py +5 -1
- nautobot/circuits/tests/test_views.py +27 -5
- nautobot/circuits/views.py +18 -10
- nautobot/core/api/__init__.py +8 -2
- nautobot/core/api/fields.py +15 -6
- nautobot/core/api/filter_backends.py +3 -2
- nautobot/core/api/metadata.py +237 -30
- nautobot/core/api/mixins.py +94 -0
- nautobot/core/api/pagination.py +4 -0
- nautobot/core/api/parsers.py +154 -0
- nautobot/core/api/renderers.py +153 -2
- nautobot/core/api/schema.py +46 -2
- nautobot/core/api/serializers.py +377 -35
- nautobot/core/api/urls.py +11 -3
- nautobot/core/api/utils.py +174 -2
- nautobot/core/api/versioning.py +32 -10
- nautobot/core/api/views.py +266 -72
- nautobot/core/apps/__init__.py +138 -220
- nautobot/core/celery/__init__.py +112 -41
- nautobot/core/celery/backends.py +19 -12
- nautobot/core/celery/control.py +46 -0
- nautobot/core/celery/encoders.py +53 -0
- nautobot/core/celery/log.py +38 -0
- nautobot/core/celery/schedulers.py +23 -4
- nautobot/core/celery/task.py +1 -16
- nautobot/core/checks.py +0 -27
- nautobot/core/choices.py +0 -113
- nautobot/core/{cli.py → cli/__init__.py} +1 -1
- nautobot/core/cli/__main__.py +3 -0
- nautobot/core/constants.py +0 -24
- nautobot/core/context_processors.py +12 -0
- nautobot/core/filters.py +2 -2
- nautobot/core/forms/__init__.py +0 -4
- nautobot/core/forms/fields.py +38 -65
- nautobot/core/forms/forms.py +4 -1
- nautobot/core/forms/utils.py +0 -52
- nautobot/core/graphql/schema.py +4 -27
- nautobot/core/jobs/__init__.py +75 -0
- nautobot/core/management/commands/build_ui.py +255 -0
- nautobot/core/management/commands/generate_test_data.py +3 -2
- nautobot/core/management/commands/post_upgrade.py +24 -24
- nautobot/core/models/__init__.py +26 -1
- nautobot/core/models/fields.py +24 -5
- nautobot/core/models/generics.py +2 -42
- nautobot/core/models/managers.py +5 -0
- nautobot/core/models/name_color_content_types.py +0 -14
- nautobot/core/models/tree_queries.py +14 -4
- nautobot/core/models/utils.py +5 -6
- nautobot/core/models/validators.py +17 -8
- nautobot/core/releases.py +8 -10
- nautobot/core/settings.py +80 -42
- nautobot/core/tables.py +5 -5
- nautobot/core/tasks.py +4 -7
- nautobot/core/templates/base.html +1 -49
- nautobot/core/templates/base_django.html +49 -0
- nautobot/core/templates/base_react.html +55 -0
- nautobot/core/templates/buttons/export.html +6 -4
- nautobot/core/templates/generic/object_bulk_create.html +10 -21
- nautobot/core/templates/generic/object_list.html +3 -1
- nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
- nautobot/core/templates/inc/footer.html +1 -0
- nautobot/core/templates/inc/javascript.html +0 -14
- nautobot/core/templates/inc/nav_menu.html +28 -33
- nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
- nautobot/core/templates/inc/relationships_table_rows.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +8 -20
- nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
- nautobot/core/templates/rest_framework/api.html +8 -0
- nautobot/core/templatetags/buttons.py +32 -28
- nautobot/core/testing/__init__.py +47 -44
- nautobot/core/testing/api.py +362 -47
- nautobot/core/testing/filters.py +1 -1
- nautobot/core/testing/migrations.py +2 -0
- nautobot/core/testing/mixins.py +22 -9
- nautobot/core/testing/schema.py +2 -1
- nautobot/core/testing/views.py +21 -46
- nautobot/core/tests/integration/test_filters.py +17 -8
- nautobot/core/tests/integration/test_navbar.py +11 -34
- nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
- nautobot/core/tests/nautobot_config.py +2 -3
- nautobot/core/tests/test_api.py +290 -21
- nautobot/core/tests/test_checks.py +0 -7
- nautobot/core/tests/test_filters.py +107 -59
- nautobot/core/tests/test_forms.py +26 -92
- nautobot/core/tests/test_graphql.py +110 -77
- nautobot/core/tests/test_logging.py +4 -0
- nautobot/core/tests/test_managers.py +3 -1
- nautobot/core/tests/test_models.py +2 -0
- nautobot/core/tests/test_paginator.py +3 -1
- nautobot/core/tests/test_releases.py +12 -12
- nautobot/core/tests/test_templatetags_helpers.py +4 -4
- nautobot/core/tests/test_utils.py +32 -68
- nautobot/core/tests/test_views.py +12 -15
- nautobot/core/utils/data.py +17 -0
- nautobot/core/utils/deprecation.py +9 -6
- nautobot/core/utils/filtering.py +8 -3
- nautobot/core/utils/git.py +12 -4
- nautobot/core/utils/lookup.py +3 -1
- nautobot/core/utils/requests.py +1 -104
- nautobot/core/views/__init__.py +1 -0
- nautobot/core/views/generic.py +75 -110
- nautobot/core/views/mixins.py +52 -61
- nautobot/core/views/renderers.py +6 -7
- nautobot/core/views/utils.py +80 -0
- nautobot/dcim/api/serializers.py +160 -667
- nautobot/dcim/api/urls.py +1 -1
- nautobot/dcim/api/views.py +7 -44
- nautobot/dcim/choices.py +2 -0
- nautobot/dcim/filters/__init__.py +21 -0
- nautobot/dcim/form_mixins.py +1 -27
- nautobot/dcim/forms.py +19 -765
- nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
- nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
- nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
- nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
- nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
- nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
- nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
- nautobot/dcim/migrations/0040_tagsfield.py +109 -0
- nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
- nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
- nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
- nautobot/dcim/models/cables.py +3 -33
- nautobot/dcim/models/device_component_templates.py +6 -0
- nautobot/dcim/models/device_components.py +12 -198
- nautobot/dcim/models/devices.py +30 -143
- nautobot/dcim/models/locations.py +3 -64
- nautobot/dcim/models/power.py +3 -50
- nautobot/dcim/models/racks.py +7 -84
- nautobot/dcim/navigation.py +141 -467
- nautobot/dcim/signals.py +0 -2
- nautobot/dcim/tables/locations.py +2 -2
- nautobot/dcim/tables/power.py +1 -2
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/devicetype.html +2 -2
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/location.html +16 -1
- nautobot/dcim/templates/dcim/locationtype.html +15 -0
- nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/rackgroup.html +0 -12
- nautobot/dcim/tests/test_api.py +166 -81
- nautobot/dcim/tests/test_cablepaths.py +41 -35
- nautobot/dcim/tests/test_filters.py +67 -23
- nautobot/dcim/tests/test_forms.py +5 -205
- nautobot/dcim/tests/test_graphql.py +7 -2
- nautobot/dcim/tests/test_migrations.py +6 -11
- nautobot/dcim/tests/test_models.py +182 -110
- nautobot/dcim/tests/test_natural_ordering.py +11 -8
- nautobot/dcim/tests/test_signals.py +6 -3
- nautobot/dcim/tests/test_views.py +197 -175
- nautobot/dcim/urls.py +11 -16
- nautobot/dcim/views.py +7 -134
- nautobot/docs/additional-features/caching.md +6 -87
- nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
- nautobot/docs/additional-features/jobs.md +177 -195
- nautobot/docs/administration/nautobot-server.md +6 -21
- nautobot/docs/administration/replicating-nautobot.md +0 -10
- nautobot/docs/configuration/optional-settings.md +32 -41
- nautobot/docs/configuration/required-settings.md +11 -52
- nautobot/docs/development/application-registry.md +2 -13
- nautobot/docs/development/extending-models.md +15 -17
- nautobot/docs/development/generic-views.md +0 -2
- nautobot/docs/development/getting-started.md +55 -5
- nautobot/docs/development/navigation-menu.md +22 -93
- nautobot/docs/development/react-ui.md +105 -0
- nautobot/docs/development/role-internals.md +1 -3
- nautobot/docs/development/style-guide.md +6 -4
- nautobot/docs/index.md +3 -2
- nautobot/docs/installation/migrating-from-netbox.md +11 -42
- nautobot/docs/installation/nautobot.md +1 -1
- nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
- nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
- nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
- nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
- nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
- nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
- nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
- nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
- nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
- nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
- nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
- nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
- nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
- nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
- nautobot/docs/models/dcim/device.md +3 -0
- nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
- nautobot/docs/models/extras/computedfield.md +4 -4
- nautobot/docs/models/extras/gitrepository.md +3 -0
- nautobot/docs/models/extras/job.md +1 -0
- nautobot/docs/models/extras/jobbutton.md +18 -13
- nautobot/docs/models/extras/jobhook.md +7 -4
- nautobot/docs/models/extras/jobresult.md +6 -2
- nautobot/docs/models/extras/relationship.md +2 -2
- nautobot/docs/models/extras/status.md +6 -19
- nautobot/docs/models/ipam/ipaddress.md +3 -0
- nautobot/docs/models/virtualization/virtualmachine.md +3 -0
- nautobot/docs/plugins/development.md +83 -21
- nautobot/docs/release-notes/version-1.5.md +53 -0
- nautobot/docs/release-notes/version-2.0.md +180 -0
- nautobot/docs/requirements.txt +1 -0
- nautobot/docs/rest-api/overview.md +384 -215
- nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
- nautobot/extras/admin.py +3 -5
- nautobot/extras/api/customfields.py +15 -39
- nautobot/extras/api/fields.py +0 -11
- nautobot/extras/api/mixins.py +45 -0
- nautobot/extras/api/relationships.py +63 -158
- nautobot/extras/api/serializers.py +165 -700
- nautobot/extras/api/urls.py +1 -1
- nautobot/extras/api/views.py +294 -280
- nautobot/extras/apps.py +4 -7
- nautobot/extras/choices.py +11 -9
- nautobot/extras/constants.py +9 -3
- nautobot/extras/datasources/__init__.py +2 -0
- nautobot/extras/datasources/git.py +135 -186
- nautobot/extras/datasources/registry.py +25 -35
- nautobot/extras/filters/__init__.py +20 -19
- nautobot/extras/filters/mixins.py +4 -4
- nautobot/extras/forms/forms.py +63 -127
- nautobot/extras/forms/mixins.py +23 -51
- nautobot/extras/health_checks.py +0 -33
- nautobot/extras/jobs.py +387 -565
- nautobot/extras/management/commands/runjob.py +24 -62
- nautobot/extras/managers.py +30 -7
- nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
- nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
- nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
- nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
- nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
- nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
- nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
- nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
- nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
- nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
- nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
- nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
- nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
- nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
- nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
- nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
- nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
- nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
- nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
- nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
- nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
- nautobot/extras/migrations/0079_tagsfield.py +28 -0
- nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
- nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
- nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
- nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
- nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
- nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
- nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
- nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
- nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
- nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
- nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
- nautobot/extras/models/__init__.py +2 -3
- nautobot/extras/models/change_logging.py +0 -36
- nautobot/extras/models/customfields.py +39 -33
- nautobot/extras/models/datasources.py +48 -50
- nautobot/extras/models/groups.py +5 -6
- nautobot/extras/models/jobs.py +189 -321
- nautobot/extras/models/mixins.py +0 -71
- nautobot/extras/models/models.py +0 -19
- nautobot/extras/models/relationships.py +19 -13
- nautobot/extras/models/roles.py +0 -34
- nautobot/extras/models/secrets.py +2 -26
- nautobot/extras/models/statuses.py +6 -5
- nautobot/extras/models/tags.py +2 -17
- nautobot/extras/navigation.py +89 -307
- nautobot/extras/plugins/__init__.py +3 -120
- nautobot/extras/plugins/utils.py +0 -3
- nautobot/extras/plugins/validators.py +5 -4
- nautobot/extras/plugins/views.py +16 -3
- nautobot/extras/querysets.py +1 -7
- nautobot/extras/registry.py +3 -0
- nautobot/extras/signals.py +26 -60
- nautobot/extras/tables.py +34 -40
- nautobot/extras/tasks.py +0 -12
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +16 -1
- nautobot/extras/templates/extras/customfield.html +0 -13
- nautobot/extras/templates/extras/gitrepository.html +3 -3
- nautobot/extras/templates/extras/inc/jobresult.html +10 -0
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
- nautobot/extras/templates/extras/job.html +35 -25
- nautobot/extras/templates/extras/job_approval_request.html +15 -30
- nautobot/extras/templates/extras/job_detail.html +13 -31
- nautobot/extras/templates/extras/job_edit.html +15 -17
- nautobot/extras/templates/extras/jobresult.html +24 -6
- nautobot/extras/templates/extras/scheduledjob.html +2 -2
- nautobot/extras/templates/extras/secret.html +28 -0
- nautobot/extras/templatetags/job_buttons.py +1 -0
- nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
- nautobot/extras/test_jobs/atomic_transaction.py +53 -0
- nautobot/extras/test_jobs/dry_run.py +29 -0
- nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
- nautobot/extras/test_jobs/duplicate_name2.py +9 -0
- nautobot/extras/test_jobs/fail.py +23 -0
- nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
- nautobot/extras/test_jobs/file_upload_pass.py +25 -0
- nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
- nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
- nautobot/extras/test_jobs/job_button_receiver.py +28 -0
- nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
- nautobot/extras/test_jobs/job_variables.py +88 -0
- nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
- nautobot/extras/test_jobs/log_redaction.py +20 -0
- nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
- nautobot/extras/test_jobs/modify_db.py +25 -0
- nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
- nautobot/extras/test_jobs/object_var_optional.py +21 -0
- nautobot/extras/test_jobs/object_var_required.py +21 -0
- nautobot/extras/test_jobs/object_vars.py +26 -0
- nautobot/extras/test_jobs/pass.py +25 -0
- nautobot/extras/test_jobs/profiling.py +32 -0
- nautobot/extras/test_jobs/read_only_job.py +15 -0
- nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
- nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
- nautobot/extras/tests/integration/test_computedfields.py +1 -1
- nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
- nautobot/extras/tests/integration/test_customfields.py +4 -2
- nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/integration/test_jobs.py +25 -27
- nautobot/extras/tests/integration/test_notes.py +8 -4
- nautobot/extras/tests/integration/test_relationships.py +2 -2
- nautobot/extras/tests/test_api.py +649 -642
- nautobot/extras/tests/test_changelog.py +3 -3
- nautobot/extras/tests/test_context_managers.py +5 -3
- nautobot/extras/tests/test_customfields.py +92 -50
- nautobot/extras/tests/test_datasources.py +189 -112
- nautobot/extras/tests/test_dynamicgroups.py +7 -8
- nautobot/extras/tests/test_filters.py +137 -89
- nautobot/extras/tests/test_forms.py +73 -75
- nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
- nautobot/extras/tests/test_jobs.py +262 -263
- nautobot/extras/tests/test_migrations.py +4 -3
- nautobot/extras/tests/test_models.py +116 -161
- nautobot/extras/tests/test_plugins.py +38 -60
- nautobot/extras/tests/test_relationships.py +167 -120
- nautobot/extras/tests/test_tags.py +6 -11
- nautobot/extras/tests/test_utils.py +31 -1
- nautobot/extras/tests/test_views.py +201 -145
- nautobot/extras/tests/test_webhooks.py +6 -2
- nautobot/extras/urls.py +42 -42
- nautobot/extras/utils.py +137 -163
- nautobot/extras/views.py +78 -152
- nautobot/ipam/api/fields.py +17 -0
- nautobot/ipam/api/serializers.py +58 -164
- nautobot/ipam/api/urls.py +1 -1
- nautobot/ipam/api/views.py +3 -2
- nautobot/ipam/apps.py +1 -2
- nautobot/ipam/filters.py +1 -10
- nautobot/ipam/forms.py +4 -177
- nautobot/ipam/lookups.py +1 -0
- nautobot/ipam/management/commands/__init__.py +0 -0
- nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
- nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
- nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
- nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
- nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
- nautobot/ipam/migrations/0028_tagsfield.py +44 -0
- nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
- nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
- nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
- nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
- nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
- nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
- nautobot/ipam/models.py +100 -236
- nautobot/ipam/navigation.py +36 -181
- nautobot/ipam/querysets.py +20 -25
- nautobot/ipam/signals.py +49 -6
- nautobot/ipam/tables.py +10 -3
- nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
- nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
- nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
- nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
- nautobot/ipam/templates/ipam/prefix.html +1 -1
- nautobot/ipam/templates/ipam/vlangroup.html +0 -13
- nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
- nautobot/ipam/tests/integration/test_prefixes.py +3 -26
- nautobot/ipam/tests/test_api.py +22 -19
- nautobot/ipam/tests/test_filters.py +59 -23
- nautobot/ipam/tests/test_migrations.py +6 -10
- nautobot/ipam/tests/test_models.py +323 -198
- nautobot/ipam/tests/test_ordering.py +2 -2
- nautobot/ipam/tests/test_querysets.py +44 -24
- nautobot/ipam/tests/test_views.py +73 -26
- nautobot/ipam/urls.py +16 -0
- nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
- nautobot/ipam/utils/migrations.py +713 -0
- nautobot/ipam/views.py +137 -20
- nautobot/project-static/docs/404.html +1178 -10
- nautobot/project-static/docs/additional-features/caching.html +1224 -159
- nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
- nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
- nautobot/project-static/docs/additional-features/graphql.html +1179 -11
- nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
- nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
- nautobot/project-static/docs/additional-features/jobs.html +1514 -328
- nautobot/project-static/docs/additional-features/napalm.html +1180 -12
- nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
- nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
- nautobot/project-static/docs/administration/celery-queues.html +1178 -10
- nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
- nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
- nautobot/project-static/docs/administration/permissions.html +1178 -10
- nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
- nautobot/project-static/docs/apps/index.html +1178 -10
- nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
- nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
- nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
- nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
- nautobot/project-static/docs/configuration/index.html +1178 -10
- nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
- nautobot/project-static/docs/configuration/required-settings.html +1312 -211
- nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
- nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
- nautobot/project-static/docs/core-functionality/devices.html +1182 -10
- nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
- nautobot/project-static/docs/core-functionality/power.html +1178 -10
- nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
- nautobot/project-static/docs/core-functionality/services.html +1178 -10
- nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
- nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
- nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
- nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
- nautobot/project-static/docs/development/application-registry.html +1190 -42
- nautobot/project-static/docs/development/best-practices.html +1178 -10
- nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
- nautobot/project-static/docs/development/extending-models.html +1238 -83
- nautobot/project-static/docs/development/generic-views.html +1180 -14
- nautobot/project-static/docs/development/getting-started.html +1365 -90
- nautobot/project-static/docs/development/homepage.html +1178 -10
- nautobot/project-static/docs/development/index.html +1178 -10
- nautobot/project-static/docs/development/model-features.html +1178 -10
- nautobot/project-static/docs/development/natural-keys.html +1178 -10
- nautobot/project-static/docs/development/navigation-menu.html +1215 -125
- nautobot/project-static/docs/development/react-ui.html +4199 -0
- nautobot/project-static/docs/development/release-checklist.html +1178 -10
- nautobot/project-static/docs/development/role-internals.html +1179 -12
- nautobot/project-static/docs/development/style-guide.html +1188 -19
- nautobot/project-static/docs/development/templates.html +1178 -10
- nautobot/project-static/docs/development/testing.html +1178 -10
- nautobot/project-static/docs/development/user-preferences.html +1178 -10
- nautobot/project-static/docs/docker/index.html +1178 -10
- nautobot/project-static/docs/index.html +1183 -12
- nautobot/project-static/docs/installation/centos.html +1178 -10
- nautobot/project-static/docs/installation/external-authentication.html +1178 -10
- nautobot/project-static/docs/installation/http-server.html +1178 -10
- nautobot/project-static/docs/installation/index.html +1178 -10
- nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
- nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
- nautobot/project-static/docs/installation/nautobot.html +1179 -11
- nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
- nautobot/project-static/docs/installation/services.html +1178 -10
- nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
- nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
- nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
- nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
- nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
- nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
- nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
- nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
- nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
- nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
- nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
- nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
- nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
- nautobot/project-static/docs/installation/ubuntu.html +1178 -10
- nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
- nautobot/project-static/docs/installation/upgrading.html +1178 -10
- nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
- nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
- nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
- nautobot/project-static/docs/models/circuits/provider.html +1293 -103
- nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
- nautobot/project-static/docs/models/dcim/cable.html +1324 -103
- nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
- nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
- nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/device.html +1326 -132
- nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
- nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
- nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
- nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
- nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/interface.html +1293 -103
- nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
- nautobot/project-static/docs/models/dcim/location.html +1293 -103
- nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
- nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
- nautobot/project-static/docs/models/dcim/platform.html +1272 -82
- nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
- nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
- nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
- nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
- nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
- nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
- nautobot/project-static/docs/models/dcim/rack.html +1272 -82
- nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
- nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
- nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
- nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
- nautobot/project-static/docs/models/dcim/region.html +1178 -10
- nautobot/project-static/docs/models/dcim/site.html +1178 -10
- nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
- nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
- nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
- nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
- nautobot/project-static/docs/models/extras/customfield.html +1180 -12
- nautobot/project-static/docs/models/extras/customlink.html +1180 -12
- nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
- nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
- nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
- nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
- nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
- nautobot/project-static/docs/models/extras/job.html +1277 -86
- nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
- nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
- nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
- nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
- nautobot/project-static/docs/models/extras/note.html +1180 -12
- nautobot/project-static/docs/models/extras/relationship.html +1182 -14
- nautobot/project-static/docs/models/extras/role.html +1320 -86
- nautobot/project-static/docs/models/extras/secret.html +1314 -86
- nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
- nautobot/project-static/docs/models/extras/status.html +1188 -59
- nautobot/project-static/docs/models/extras/tag.html +1180 -12
- nautobot/project-static/docs/models/extras/webhook.html +1180 -12
- nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
- nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
- nautobot/project-static/docs/models/ipam/rir.html +1276 -86
- nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
- nautobot/project-static/docs/models/ipam/service.html +1276 -86
- nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
- nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
- nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
- nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
- nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
- nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
- nautobot/project-static/docs/models/users/token.html +1276 -86
- nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
- nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
- nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
- nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
- nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/plugins/development.html +1726 -495
- nautobot/project-static/docs/plugins/index.html +1178 -10
- nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
- nautobot/project-static/docs/release-notes/index.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
- nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
- nautobot/project-static/docs/requirements.txt +1 -0
- nautobot/project-static/docs/rest-api/authentication.html +1179 -11
- nautobot/project-static/docs/rest-api/filtering.html +1178 -10
- nautobot/project-static/docs/rest-api/overview.html +1841 -446
- nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +197 -187
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
- nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
- nautobot/project-static/docs/user-guides/graphql.html +1178 -10
- nautobot/project-static/docs/user-guides/relationships.html +1178 -10
- nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
- nautobot/project-static/js/forms.js +16 -9
- nautobot/project-static/js/theme.js +5 -0
- nautobot/tenancy/api/serializers.py +4 -32
- nautobot/tenancy/api/urls.py +1 -1
- nautobot/tenancy/forms.py +0 -28
- nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
- nautobot/tenancy/models.py +0 -25
- nautobot/tenancy/navigation.py +6 -39
- nautobot/tenancy/templates/tenancy/tenant.html +12 -12
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- nautobot/tenancy/tests/test_api.py +1 -3
- nautobot/tenancy/tests/test_filters.py +10 -5
- nautobot/tenancy/views.py +0 -2
- nautobot/ui/.eslintignore +6 -0
- nautobot/ui/.gitignore +10 -0
- nautobot/ui/.prettierignore +9 -0
- nautobot/ui/.prettierrc +4 -0
- nautobot/ui/README.md +33 -0
- nautobot/ui/app_imports.js.j2 +7 -0
- nautobot/ui/craco.config.js +46 -0
- nautobot/ui/jsconfig-base.json +11 -0
- nautobot/ui/jsconfig.json +5 -0
- nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
- nautobot/ui/package-lock.json +21451 -0
- nautobot/ui/package.json +70 -0
- nautobot/ui/public/index.html +47 -0
- nautobot/ui/public/logo192.png +0 -0
- nautobot/ui/public/logo512.png +0 -0
- nautobot/ui/public/manifest.json +25 -0
- nautobot/ui/public/nautobot_logo.svg +131 -0
- nautobot/ui/public/robots.txt +3 -0
- nautobot/ui/src/App.js +71 -0
- nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
- nautobot/ui/src/components/AppTab.js +40 -0
- nautobot/ui/src/components/Apps.js +60 -0
- nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
- nautobot/ui/src/components/HomePanel.js +58 -0
- nautobot/ui/src/components/JobHistoryTable.js +78 -0
- nautobot/ui/src/components/Layout.js +53 -0
- nautobot/ui/src/components/LoadingWidget.js +25 -0
- nautobot/ui/src/components/Navbar.js +116 -0
- nautobot/ui/src/components/NotificationPopover.js +27 -0
- nautobot/ui/src/components/ObjectListTable.js +209 -0
- nautobot/ui/src/components/ReferenceDataTag.js +35 -0
- nautobot/ui/src/components/RouterButton.js +10 -0
- nautobot/ui/src/components/RouterLink.js +10 -0
- nautobot/ui/src/components/SidebarNav.js +147 -0
- nautobot/ui/src/components/Table.js +48 -0
- nautobot/ui/src/components/TableItem.js +71 -0
- nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
- nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
- nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
- nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
- nautobot/ui/src/components/__tests__/Table.test.js +36 -0
- nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
- nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
- nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
- nautobot/ui/src/components/pagination.js +93 -0
- nautobot/ui/src/components/paginator.js +79 -0
- nautobot/ui/src/components/paginator_form.js +43 -0
- nautobot/ui/src/components/usePagination.js +57 -0
- nautobot/ui/src/constants/apiPath.js +10 -0
- nautobot/ui/src/constants/icons.js +15 -0
- nautobot/ui/src/constants/size.js +15 -0
- nautobot/ui/src/index.js +65 -0
- nautobot/ui/src/reportWebVitals.js +15 -0
- nautobot/ui/src/router.js +77 -0
- nautobot/ui/src/utils/api.js +131 -0
- nautobot/ui/src/utils/app-import.js +15 -0
- nautobot/ui/src/utils/color.js +15 -0
- nautobot/ui/src/utils/date.js +14 -0
- nautobot/ui/src/utils/index.js +15 -0
- nautobot/ui/src/utils/navigation.js +32 -0
- nautobot/ui/src/utils/session.js +64 -0
- nautobot/ui/src/utils/store.js +242 -0
- nautobot/ui/src/utils/string.js +6 -0
- nautobot/ui/src/utils/url.js +4 -0
- nautobot/ui/src/views/Home.js +138 -0
- nautobot/ui/src/views/InstalledApps.js +80 -0
- nautobot/ui/src/views/Login.js +48 -0
- nautobot/ui/src/views/Logout.js +20 -0
- nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
- nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
- nautobot/ui/src/views/__tests__/Login.test.js +15 -0
- nautobot/ui/src/views/generic/GenericView.js +142 -0
- nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
- nautobot/ui/src/views/generic/ObjectList.js +127 -0
- nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +51 -61
- nautobot/users/api/urls.py +1 -1
- nautobot/users/api/views.py +53 -2
- nautobot/users/tests/test_api.py +110 -25
- nautobot/virtualization/api/serializers.py +18 -130
- nautobot/virtualization/api/urls.py +1 -1
- nautobot/virtualization/api/views.py +1 -22
- nautobot/virtualization/forms.py +13 -99
- nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
- nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
- nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
- nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
- nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
- nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
- nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
- nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
- nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
- nautobot/virtualization/models.py +31 -123
- nautobot/virtualization/navigation.py +18 -99
- nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
- nautobot/virtualization/tests/test_api.py +25 -26
- nautobot/virtualization/tests/test_filters.py +41 -15
- nautobot/virtualization/tests/test_models.py +31 -7
- nautobot/virtualization/tests/test_views.py +42 -25
- nautobot/virtualization/views.py +7 -6
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
- nautobot/circuits/api/nested_serializers.py +0 -69
- nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
- nautobot/dcim/api/nested_serializers.py +0 -356
- nautobot/dcim/templates/dcim/device_import.html +0 -5
- nautobot/dcim/templates/dcim/device_import_child.html +0 -5
- nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
- nautobot/extras/api/nested_serializers.py +0 -353
- nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
- nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
- nautobot/extras/reports.py +0 -60
- nautobot/extras/scripts.py +0 -72
- nautobot/extras/tests/example_jobs/script_variables.py +0 -67
- nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
- nautobot/extras/tests/example_jobs/test_fail.py +0 -16
- nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
- nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
- nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
- nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
- nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
- nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
- nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
- nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
- nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
- nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
- nautobot/extras/tests/example_jobs/test_pass.py +0 -19
- nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
- nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
- nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
- nautobot/ipam/api/nested_serializers.py +0 -159
- nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
- nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
- nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
- nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
- nautobot/tenancy/api/nested_serializers.py +0 -31
- nautobot/users/api/nested_serializers.py +0 -67
- nautobot/virtualization/api/nested_serializers.py +0 -65
- /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
- /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -162,7 +162,7 @@ class ComputedFieldFilterSet(BaseFilterSet):
|
|
|
162
162
|
model = ComputedField
|
|
163
163
|
fields = (
|
|
164
164
|
"content_type",
|
|
165
|
-
"
|
|
165
|
+
"key",
|
|
166
166
|
"template",
|
|
167
167
|
"fallback_value",
|
|
168
168
|
"weight",
|
|
@@ -553,7 +553,6 @@ class JobFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
553
553
|
q = SearchFilter(
|
|
554
554
|
filter_predicates={
|
|
555
555
|
"name": "icontains",
|
|
556
|
-
"slug": "icontains",
|
|
557
556
|
"grouping": "icontains",
|
|
558
557
|
"description": "icontains",
|
|
559
558
|
},
|
|
@@ -563,17 +562,15 @@ class JobFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
563
562
|
model = Job
|
|
564
563
|
fields = [
|
|
565
564
|
"id",
|
|
566
|
-
"source",
|
|
567
565
|
"module_name",
|
|
568
566
|
"job_class_name",
|
|
569
|
-
"slug",
|
|
570
567
|
"name",
|
|
571
568
|
"grouping",
|
|
572
569
|
"installed",
|
|
573
570
|
"enabled",
|
|
574
571
|
"has_sensitive_variables",
|
|
575
572
|
"approval_required",
|
|
576
|
-
"
|
|
573
|
+
"dryrun_default",
|
|
577
574
|
"hidden",
|
|
578
575
|
"read_only",
|
|
579
576
|
"is_job_hook_receiver",
|
|
@@ -584,9 +581,8 @@ class JobFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
584
581
|
"name_override",
|
|
585
582
|
"approval_required_override",
|
|
586
583
|
"description_override",
|
|
587
|
-
"
|
|
584
|
+
"dryrun_default_override",
|
|
588
585
|
"hidden_override",
|
|
589
|
-
"read_only_override",
|
|
590
586
|
"soft_time_limit_override",
|
|
591
587
|
"time_limit_override",
|
|
592
588
|
"has_sensitive_variables_override",
|
|
@@ -600,8 +596,9 @@ class JobHookFilterSet(BaseFilterSet):
|
|
|
600
596
|
choices=ChangeLoggedModelsQuery().get_choices,
|
|
601
597
|
)
|
|
602
598
|
job = NaturalKeyOrPKMultipleChoiceFilter(
|
|
599
|
+
to_field_name="name",
|
|
603
600
|
queryset=Job.objects.all(),
|
|
604
|
-
label="Job (
|
|
601
|
+
label="Job (name or ID)",
|
|
605
602
|
)
|
|
606
603
|
|
|
607
604
|
class Meta:
|
|
@@ -626,21 +623,21 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
626
623
|
},
|
|
627
624
|
)
|
|
628
625
|
job_model = NaturalKeyOrPKMultipleChoiceFilter(
|
|
626
|
+
to_field_name="name",
|
|
629
627
|
queryset=Job.objects.all(),
|
|
630
|
-
label="Job (
|
|
628
|
+
label="Job (name or ID)",
|
|
631
629
|
)
|
|
632
630
|
job_model_id = django_filters.ModelMultipleChoiceFilter(
|
|
633
631
|
queryset=Job.objects.all(),
|
|
634
632
|
label="Job (ID) - Deprecated (use job_model filter)",
|
|
635
633
|
)
|
|
636
|
-
obj_type = ContentTypeFilter()
|
|
637
634
|
date_created = django_filters.DateTimeFilter()
|
|
638
635
|
date_done = django_filters.DateTimeFilter()
|
|
639
636
|
status = django_filters.MultipleChoiceFilter(choices=JobResultStatusChoices, null_value=None)
|
|
640
637
|
|
|
641
638
|
class Meta:
|
|
642
639
|
model = JobResult
|
|
643
|
-
fields = ["id", "date_created", "date_done", "name", "status", "user"
|
|
640
|
+
fields = ["id", "date_created", "date_done", "name", "status", "user"]
|
|
644
641
|
|
|
645
642
|
|
|
646
643
|
class JobLogEntryFilterSet(BaseFilterSet):
|
|
@@ -666,9 +663,9 @@ class ScheduledJobFilterSet(BaseFilterSet):
|
|
|
666
663
|
},
|
|
667
664
|
)
|
|
668
665
|
job_model = NaturalKeyOrPKMultipleChoiceFilter(
|
|
669
|
-
|
|
666
|
+
to_field_name="name",
|
|
670
667
|
queryset=Job.objects.all(),
|
|
671
|
-
label="Job (
|
|
668
|
+
label="Job (name or ID)",
|
|
672
669
|
)
|
|
673
670
|
job_model_id = django_filters.ModelMultipleChoiceFilter(
|
|
674
671
|
queryset=Job.objects.all(),
|
|
@@ -697,7 +694,11 @@ class JobButtonFilterSet(BaseFilterSet):
|
|
|
697
694
|
},
|
|
698
695
|
)
|
|
699
696
|
content_types = ContentTypeFilter()
|
|
700
|
-
job = NaturalKeyOrPKMultipleChoiceFilter(
|
|
697
|
+
job = NaturalKeyOrPKMultipleChoiceFilter(
|
|
698
|
+
to_field_name="name",
|
|
699
|
+
queryset=Job.objects.all(),
|
|
700
|
+
label="Job (name or ID)",
|
|
701
|
+
)
|
|
701
702
|
|
|
702
703
|
class Meta:
|
|
703
704
|
model = JobButton
|
|
@@ -792,7 +793,7 @@ class ObjectChangeFilterSet(BaseFilterSet):
|
|
|
792
793
|
class RelationshipFilterSet(BaseFilterSet):
|
|
793
794
|
q = SearchFilter(
|
|
794
795
|
filter_predicates={
|
|
795
|
-
"
|
|
796
|
+
"label": "icontains",
|
|
796
797
|
"description": "icontains",
|
|
797
798
|
}
|
|
798
799
|
)
|
|
@@ -804,15 +805,15 @@ class RelationshipFilterSet(BaseFilterSet):
|
|
|
804
805
|
|
|
805
806
|
class Meta:
|
|
806
807
|
model = Relationship
|
|
807
|
-
fields = ["id", "
|
|
808
|
+
fields = ["id", "label", "key", "type", "source_type", "destination_type"]
|
|
808
809
|
|
|
809
810
|
|
|
810
811
|
class RelationshipAssociationFilterSet(BaseFilterSet):
|
|
811
812
|
relationship = django_filters.ModelMultipleChoiceFilter(
|
|
812
|
-
field_name="
|
|
813
|
+
field_name="relationship__key",
|
|
813
814
|
queryset=Relationship.objects.all(),
|
|
814
|
-
to_field_name="
|
|
815
|
-
label="Relationship (
|
|
815
|
+
to_field_name="key",
|
|
816
|
+
label="Relationship (key)",
|
|
816
817
|
)
|
|
817
818
|
source_type = ContentTypeMultipleChoiceFilter(choices=FeatureQuery("relationships").get_choices, conjoined=False)
|
|
818
819
|
destination_type = ContentTypeMultipleChoiceFilter(
|
|
@@ -254,10 +254,10 @@ class RelationshipModelFilterSetMixin(django_filters.FilterSet):
|
|
|
254
254
|
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
255
255
|
|
|
256
256
|
# If this model is on the "source" side of the relationship, then the field will be named
|
|
257
|
-
# "cr_<
|
|
258
|
-
# If we're on the "destination" side, the field will be "cr_<
|
|
259
|
-
# For a symmetric relationship, both sides are "peer", so the field will be "cr_<
|
|
260
|
-
field_name = f"cr_{relationship.
|
|
257
|
+
# "cr_<relationship_key>__destination" since it's used to pick the destination object(s).
|
|
258
|
+
# If we're on the "destination" side, the field will be "cr_<relationship_key>__source".
|
|
259
|
+
# For a symmetric relationship, both sides are "peer", so the field will be "cr_<relationship_key>__peer"
|
|
260
|
+
field_name = f"cr_{relationship.key}__{peer_side}"
|
|
261
261
|
|
|
262
262
|
if field_name in self.relationships:
|
|
263
263
|
# This is a symmetric relationship that we already processed from the opposing "initial_side".
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -6,7 +6,6 @@ from django.core.exceptions import ValidationError
|
|
|
6
6
|
from django.db.models.fields import TextField
|
|
7
7
|
from django.forms import ModelMultipleChoiceField, inlineformset_factory
|
|
8
8
|
from django.urls.base import reverse
|
|
9
|
-
from django.utils.safestring import mark_safe
|
|
10
9
|
|
|
11
10
|
from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
|
|
12
11
|
from nautobot.core.forms import (
|
|
@@ -19,10 +18,7 @@ from nautobot.core.forms import (
|
|
|
19
18
|
ColorSelect,
|
|
20
19
|
CommentField,
|
|
21
20
|
CSVContentTypeField,
|
|
22
|
-
CSVModelChoiceField,
|
|
23
21
|
CSVModelForm,
|
|
24
|
-
CSVMultipleChoiceField,
|
|
25
|
-
CSVMultipleContentTypeField,
|
|
26
22
|
DateTimePicker,
|
|
27
23
|
DynamicModelChoiceField,
|
|
28
24
|
DynamicModelMultipleChoiceField,
|
|
@@ -113,7 +109,6 @@ __all__ = (
|
|
|
113
109
|
"ExportTemplateForm",
|
|
114
110
|
"ExportTemplateFilterForm",
|
|
115
111
|
"GitRepositoryForm",
|
|
116
|
-
"GitRepositoryCSVForm",
|
|
117
112
|
"GitRepositoryBulkEditForm",
|
|
118
113
|
"GitRepositoryFilterForm",
|
|
119
114
|
"GraphQLQueryForm",
|
|
@@ -139,21 +134,17 @@ __all__ = (
|
|
|
139
134
|
"RelationshipFilterForm",
|
|
140
135
|
"RelationshipAssociationFilterForm",
|
|
141
136
|
"RoleBulkEditForm",
|
|
142
|
-
"RoleCSVForm",
|
|
143
137
|
"RoleForm",
|
|
144
138
|
"ScheduledJobFilterForm",
|
|
145
139
|
"SecretForm",
|
|
146
|
-
"SecretCSVForm",
|
|
147
140
|
"SecretFilterForm",
|
|
148
141
|
"SecretsGroupForm",
|
|
149
142
|
"SecretsGroupFilterForm",
|
|
150
143
|
"SecretsGroupAssociationFormSet",
|
|
151
144
|
"StatusForm",
|
|
152
|
-
"StatusCSVForm",
|
|
153
145
|
"StatusFilterForm",
|
|
154
146
|
"StatusBulkEditForm",
|
|
155
147
|
"TagForm",
|
|
156
|
-
"TagCSVForm",
|
|
157
148
|
"TagFilterForm",
|
|
158
149
|
"TagBulkEditForm",
|
|
159
150
|
"WebhookForm",
|
|
@@ -172,7 +163,9 @@ class ComputedFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
172
163
|
required=True,
|
|
173
164
|
label="Content Type",
|
|
174
165
|
)
|
|
175
|
-
|
|
166
|
+
key = SlugField(
|
|
167
|
+
label="Key",
|
|
168
|
+
max_length=50,
|
|
176
169
|
slug_source="label",
|
|
177
170
|
help_text="Internal name of this field. Please use underscores rather than dashes.",
|
|
178
171
|
)
|
|
@@ -189,7 +182,7 @@ class ComputedFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
189
182
|
fields = (
|
|
190
183
|
"content_type",
|
|
191
184
|
"label",
|
|
192
|
-
"
|
|
185
|
+
"key",
|
|
193
186
|
"description",
|
|
194
187
|
"template",
|
|
195
188
|
"fallback_value",
|
|
@@ -197,6 +190,12 @@ class ComputedFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
197
190
|
"advanced_ui",
|
|
198
191
|
)
|
|
199
192
|
|
|
193
|
+
def __init__(self, *args, **kwargs):
|
|
194
|
+
super().__init__(*args, **kwargs)
|
|
195
|
+
|
|
196
|
+
if self.instance and self.instance.present_in_database:
|
|
197
|
+
self.fields["key"].widget.attrs["readonly"] = True
|
|
198
|
+
|
|
200
199
|
|
|
201
200
|
class ComputedFieldFilterForm(BootstrapMixin, forms.Form):
|
|
202
201
|
model = ComputedField
|
|
@@ -401,9 +400,21 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
401
400
|
"validation_regex",
|
|
402
401
|
)
|
|
403
402
|
|
|
403
|
+
def __init__(self, *args, **kwargs):
|
|
404
|
+
super().__init__(*args, **kwargs)
|
|
405
|
+
|
|
406
|
+
if self.instance and self.instance.present_in_database:
|
|
407
|
+
self.fields["key"].widget.attrs["readonly"] = True
|
|
408
|
+
|
|
404
409
|
|
|
405
410
|
class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelFormMixin):
|
|
406
|
-
"""
|
|
411
|
+
"""
|
|
412
|
+
Base class for CSV/JSON/YAML import of models that support custom fields.
|
|
413
|
+
|
|
414
|
+
TODO: The class name is a misnomer; as of 2.0 this class is **not** used for any CSV imports,
|
|
415
|
+
as that's now handled by the REST API. However it is still used when importing component-templates as
|
|
416
|
+
part of a JSON/YAML DeviceType import.
|
|
417
|
+
"""
|
|
407
418
|
|
|
408
419
|
def _append_customfield_fields(self):
|
|
409
420
|
# Append form fields
|
|
@@ -611,36 +622,24 @@ class GitRepositoryForm(BootstrapMixin, RelationshipModelFormMixin):
|
|
|
611
622
|
"tags",
|
|
612
623
|
]
|
|
613
624
|
|
|
614
|
-
def
|
|
615
|
-
super().
|
|
616
|
-
|
|
617
|
-
# set dryrun after a successful clean
|
|
618
|
-
if "_dryrun_create" in self.data or "_dryrun_update" in self.data:
|
|
619
|
-
self.instance.set_dryrun()
|
|
625
|
+
def __init__(self, *args, **kwargs):
|
|
626
|
+
super().__init__(*args, **kwargs)
|
|
620
627
|
|
|
628
|
+
if self.instance and self.instance.present_in_database:
|
|
629
|
+
self.fields["slug"].widget.attrs["readonly"] = True
|
|
621
630
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
queryset=SecretsGroup.objects.all(),
|
|
625
|
-
to_field_name="name",
|
|
626
|
-
required=False,
|
|
627
|
-
help_text="Secrets group for repository access (if any)",
|
|
628
|
-
)
|
|
631
|
+
def save(self, commit=True):
|
|
632
|
+
instance = super().save(commit=commit)
|
|
629
633
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
634
|
+
# TODO(jathan): Move sync() call out of the form and into the view. However, in v2 UI this
|
|
635
|
+
# probably just goes away since UI views will be making API calls. For now, the user is
|
|
636
|
+
# magically stored on the instance by the view code.
|
|
637
|
+
if commit:
|
|
638
|
+
# Set dryrun if that button was clicked in the UI, otherwise perform a normal sync.
|
|
639
|
+
dry_run = "_dryrun_create" in self.data or "_dryrun_update" in self.data
|
|
640
|
+
instance.sync(user=instance.user, dry_run=dry_run)
|
|
633
641
|
|
|
634
|
-
|
|
635
|
-
super().__init__(*args, **kwargs)
|
|
636
|
-
self.fields["provided_contents"] = CSVMultipleChoiceField(
|
|
637
|
-
choices=get_git_datasource_content_choices(),
|
|
638
|
-
required=False,
|
|
639
|
-
help_text=mark_safe(
|
|
640
|
-
"The data types this repository provides. Multiple values must be comma-separated and wrapped in "
|
|
641
|
-
'double quotes (e.g. <code>"extras.job,extras.configcontext"</code>).'
|
|
642
|
-
),
|
|
643
|
-
)
|
|
642
|
+
return instance
|
|
644
643
|
|
|
645
644
|
|
|
646
645
|
class GitRepositoryBulkEditForm(NautobotBulkEditForm):
|
|
@@ -723,11 +722,11 @@ class JobForm(BootstrapMixin, forms.Form):
|
|
|
723
722
|
controlled by the job definition. See `nautobot.extras.jobs.BaseJob.as_form`
|
|
724
723
|
"""
|
|
725
724
|
|
|
726
|
-
|
|
725
|
+
_profile = forms.BooleanField(
|
|
727
726
|
required=False,
|
|
728
|
-
initial=
|
|
729
|
-
label="
|
|
730
|
-
help_text="
|
|
727
|
+
initial=False,
|
|
728
|
+
label="Profile job execution",
|
|
729
|
+
help_text="Profiles the job execution using cProfile and outputs a report to /tmp/",
|
|
731
730
|
)
|
|
732
731
|
_task_queue = forms.ChoiceField(
|
|
733
732
|
required=False,
|
|
@@ -738,27 +737,16 @@ class JobForm(BootstrapMixin, forms.Form):
|
|
|
738
737
|
def __init__(self, *args, **kwargs):
|
|
739
738
|
super().__init__(*args, **kwargs)
|
|
740
739
|
|
|
741
|
-
# Move
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
self.fields["_commit"] = commit
|
|
746
|
-
|
|
747
|
-
@property
|
|
748
|
-
def requires_input(self):
|
|
749
|
-
"""
|
|
750
|
-
A boolean indicating whether the form requires user input (ignore the _commit field).
|
|
751
|
-
"""
|
|
752
|
-
return bool(len(self.fields) > 1)
|
|
740
|
+
# Move special fields to the end of the form
|
|
741
|
+
for field in ["_task_queue", "_profile"]:
|
|
742
|
+
value = self.fields.pop(field)
|
|
743
|
+
self.fields[field] = value
|
|
753
744
|
|
|
754
745
|
|
|
755
746
|
class JobEditForm(NautobotModelForm):
|
|
756
|
-
slug = SlugField()
|
|
757
|
-
|
|
758
747
|
class Meta:
|
|
759
748
|
model = Job
|
|
760
749
|
fields = [
|
|
761
|
-
"slug",
|
|
762
750
|
"enabled",
|
|
763
751
|
"name_override",
|
|
764
752
|
"name",
|
|
@@ -766,12 +754,10 @@ class JobEditForm(NautobotModelForm):
|
|
|
766
754
|
"grouping",
|
|
767
755
|
"description_override",
|
|
768
756
|
"description",
|
|
769
|
-
"
|
|
770
|
-
"
|
|
757
|
+
"dryrun_default_override",
|
|
758
|
+
"dryrun_default",
|
|
771
759
|
"hidden_override",
|
|
772
760
|
"hidden",
|
|
773
|
-
"read_only_override",
|
|
774
|
-
"read_only",
|
|
775
761
|
"approval_required_override",
|
|
776
762
|
"approval_required",
|
|
777
763
|
"soft_time_limit_override",
|
|
@@ -810,7 +796,7 @@ class JobFilterForm(BootstrapMixin, forms.Form):
|
|
|
810
796
|
has_sensitive_variables = forms.NullBooleanField(
|
|
811
797
|
required=False, widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES)
|
|
812
798
|
)
|
|
813
|
-
|
|
799
|
+
dryrun_default = forms.NullBooleanField(required=False, widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES))
|
|
814
800
|
hidden = forms.NullBooleanField(
|
|
815
801
|
initial=False,
|
|
816
802
|
required=False,
|
|
@@ -1138,7 +1124,12 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
|
|
|
1138
1124
|
|
|
1139
1125
|
|
|
1140
1126
|
class RelationshipForm(BootstrapMixin, forms.ModelForm):
|
|
1141
|
-
|
|
1127
|
+
key = SlugField(
|
|
1128
|
+
help_text="Internal name of this relationship. Please use underscores rather than dashes.",
|
|
1129
|
+
label="Key",
|
|
1130
|
+
max_length=50,
|
|
1131
|
+
slug_source="label",
|
|
1132
|
+
)
|
|
1142
1133
|
source_type = forms.ModelChoiceField(
|
|
1143
1134
|
queryset=ContentType.objects.filter(FeatureQuery("relationships").get_query()).order_by("app_label", "model"),
|
|
1144
1135
|
help_text="The source object type to which this relationship applies.",
|
|
@@ -1161,8 +1152,8 @@ class RelationshipForm(BootstrapMixin, forms.ModelForm):
|
|
|
1161
1152
|
class Meta:
|
|
1162
1153
|
model = Relationship
|
|
1163
1154
|
fields = [
|
|
1164
|
-
"
|
|
1165
|
-
"
|
|
1155
|
+
"label",
|
|
1156
|
+
"key",
|
|
1166
1157
|
"description",
|
|
1167
1158
|
"type",
|
|
1168
1159
|
"required_on",
|
|
@@ -1177,6 +1168,12 @@ class RelationshipForm(BootstrapMixin, forms.ModelForm):
|
|
|
1177
1168
|
"destination_filter",
|
|
1178
1169
|
]
|
|
1179
1170
|
|
|
1171
|
+
def __init__(self, *args, **kwargs):
|
|
1172
|
+
super().__init__(*args, **kwargs)
|
|
1173
|
+
|
|
1174
|
+
if self.instance and self.instance.present_in_database:
|
|
1175
|
+
self.fields["key"].widget.attrs["readonly"] = True
|
|
1176
|
+
|
|
1180
1177
|
def save(self, commit=True):
|
|
1181
1178
|
# TODO add support for owner when a CR is created in the UI
|
|
1182
1179
|
obj = super().save(commit)
|
|
@@ -1203,7 +1200,7 @@ class RelationshipAssociationFilterForm(BootstrapMixin, forms.Form):
|
|
|
1203
1200
|
|
|
1204
1201
|
relationship = DynamicModelMultipleChoiceField(
|
|
1205
1202
|
queryset=Relationship.objects.all(),
|
|
1206
|
-
to_field_name="
|
|
1203
|
+
to_field_name="key",
|
|
1207
1204
|
required=False,
|
|
1208
1205
|
)
|
|
1209
1206
|
|
|
@@ -1249,28 +1246,6 @@ class RoleBulkEditForm(NautobotBulkEditForm):
|
|
|
1249
1246
|
nullable_fields = ["weight"]
|
|
1250
1247
|
|
|
1251
1248
|
|
|
1252
|
-
class RoleCSVForm(CustomFieldModelCSVForm):
|
|
1253
|
-
"""Generic CSV bulk import form for `Role` objects."""
|
|
1254
|
-
|
|
1255
|
-
content_types = CSVMultipleContentTypeField(
|
|
1256
|
-
queryset=RoleModelsQuery().as_queryset(),
|
|
1257
|
-
choices_as_strings=True,
|
|
1258
|
-
help_text=mark_safe(
|
|
1259
|
-
"The object types to which this role applies. Multiple values "
|
|
1260
|
-
"must be comma-separated and wrapped in double quotes. (e.g. "
|
|
1261
|
-
'<code>"dcim.device,dcim.rack"</code>)'
|
|
1262
|
-
),
|
|
1263
|
-
label="Content type(s)",
|
|
1264
|
-
)
|
|
1265
|
-
|
|
1266
|
-
class Meta:
|
|
1267
|
-
model = Role
|
|
1268
|
-
fields = Role.csv_headers
|
|
1269
|
-
help_texts = {
|
|
1270
|
-
"color": mark_safe("RGB color in hexadecimal (e.g. <code>00ff00</code>)"),
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
1249
|
#
|
|
1275
1250
|
# Secrets
|
|
1276
1251
|
#
|
|
@@ -1298,12 +1273,6 @@ class SecretForm(NautobotModelForm):
|
|
|
1298
1273
|
]
|
|
1299
1274
|
|
|
1300
1275
|
|
|
1301
|
-
class SecretCSVForm(CustomFieldModelCSVForm):
|
|
1302
|
-
class Meta:
|
|
1303
|
-
model = Secret
|
|
1304
|
-
fields = Secret.csv_headers
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
1276
|
def provider_choices_with_blank():
|
|
1308
1277
|
return add_blank_choice(sorted([(slug, provider.name) for slug, provider in registry["secrets_providers"].items()]))
|
|
1309
1278
|
|
|
@@ -1363,28 +1332,6 @@ class StatusForm(NautobotModelForm):
|
|
|
1363
1332
|
fields = ["name", "description", "content_types", "color"]
|
|
1364
1333
|
|
|
1365
1334
|
|
|
1366
|
-
class StatusCSVForm(CustomFieldModelCSVForm):
|
|
1367
|
-
"""Generic CSV bulk import form for `Status` objects."""
|
|
1368
|
-
|
|
1369
|
-
content_types = CSVMultipleContentTypeField(
|
|
1370
|
-
feature="statuses",
|
|
1371
|
-
choices_as_strings=True,
|
|
1372
|
-
help_text=mark_safe(
|
|
1373
|
-
"The object types to which this status applies. Multiple values "
|
|
1374
|
-
"must be comma-separated and wrapped in double quotes. (e.g. "
|
|
1375
|
-
'<code>"dcim.device,dcim.rack"</code>)'
|
|
1376
|
-
),
|
|
1377
|
-
label="Content type(s)",
|
|
1378
|
-
)
|
|
1379
|
-
|
|
1380
|
-
class Meta:
|
|
1381
|
-
model = Status
|
|
1382
|
-
fields = Status.csv_headers
|
|
1383
|
-
help_texts = {
|
|
1384
|
-
"color": mark_safe("RGB color in hexadecimal (e.g. <code>00ff00</code>)"),
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
1335
|
class StatusFilterForm(NautobotFilterForm):
|
|
1389
1336
|
"""Filtering/search form for `Status` objects."""
|
|
1390
1337
|
|
|
@@ -1437,17 +1384,6 @@ class TagForm(NautobotModelForm):
|
|
|
1437
1384
|
return data
|
|
1438
1385
|
|
|
1439
1386
|
|
|
1440
|
-
class TagCSVForm(CustomFieldModelCSVForm):
|
|
1441
|
-
slug = SlugField()
|
|
1442
|
-
|
|
1443
|
-
class Meta:
|
|
1444
|
-
model = Tag
|
|
1445
|
-
fields = Tag.csv_headers
|
|
1446
|
-
help_texts = {
|
|
1447
|
-
"color": mark_safe("RGB color in hexadecimal (e.g. <code>00ff00</code>)"),
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
1387
|
class TagFilterForm(NautobotFilterForm):
|
|
1452
1388
|
model = Tag
|
|
1453
1389
|
q = forms.CharField(required=False, label="Search")
|
nautobot/extras/forms/mixins.py
CHANGED
|
@@ -8,8 +8,6 @@ from django.db.models import Q
|
|
|
8
8
|
from nautobot.core.forms import (
|
|
9
9
|
BulkEditForm,
|
|
10
10
|
CommentField,
|
|
11
|
-
CSVModelChoiceField,
|
|
12
|
-
CSVModelForm,
|
|
13
11
|
DynamicModelChoiceField,
|
|
14
12
|
DynamicModelMultipleChoiceField,
|
|
15
13
|
)
|
|
@@ -44,7 +42,6 @@ __all__ = (
|
|
|
44
42
|
"RelationshipModelFormMixin",
|
|
45
43
|
"StatusModelBulkEditFormMixin",
|
|
46
44
|
"StatusModelFilterFormMixin",
|
|
47
|
-
"StatusModelCSVFormMixin",
|
|
48
45
|
"TagsBulkEditFormMixin",
|
|
49
46
|
# 2.0 TODO: remove the below deprecated aliases
|
|
50
47
|
"AddRemoveTagsForm",
|
|
@@ -53,9 +50,7 @@ __all__ = (
|
|
|
53
50
|
"CustomFieldModelForm",
|
|
54
51
|
"RelationshipModelForm",
|
|
55
52
|
"RoleModelBulkEditFormMixin",
|
|
56
|
-
"RoleModelCSVFormMixin",
|
|
57
53
|
"RoleModelFilterFormMixin",
|
|
58
|
-
"RoleRequiredRoleModelCSVFormMixin",
|
|
59
54
|
"StatusBulkEditFormMixin",
|
|
60
55
|
"StatusFilterFormMixin",
|
|
61
56
|
)
|
|
@@ -214,10 +209,10 @@ class RelationshipModelBulkEditFormMixin(BulkEditForm):
|
|
|
214
209
|
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
215
210
|
|
|
216
211
|
# If this model is on the "source" side of the relationship, then the field will be named
|
|
217
|
-
# "cr_<
|
|
218
|
-
# If we're on the "destination" side, the field will be "cr_<
|
|
219
|
-
# For a symmetric relationship, both sides are "peer", so the field will be "cr_<
|
|
220
|
-
field_name = f"cr_{relationship.
|
|
212
|
+
# "cr_<relationship_key>__destination" since it's used to pick the destination object(s).
|
|
213
|
+
# If we're on the "destination" side, the field will be "cr_<relationship_key>__source".
|
|
214
|
+
# For a symmetric relationship, both sides are "peer", so the field will be "cr_<relationship_key>__peer"
|
|
215
|
+
field_name = f"cr_{relationship.key}__{peer_side}"
|
|
221
216
|
|
|
222
217
|
if field_name in self.relationships:
|
|
223
218
|
# This is a symmetric relationship that we already processed from the opposing "initial_side".
|
|
@@ -257,7 +252,7 @@ class RelationshipModelBulkEditFormMixin(BulkEditForm):
|
|
|
257
252
|
for side, relationships_data in instance_relationships.items():
|
|
258
253
|
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
259
254
|
for relationship, relationshipassociation_queryset in relationships_data.items():
|
|
260
|
-
field_name = f"cr_{relationship.
|
|
255
|
+
field_name = f"cr_{relationship.key}__{peer_side}"
|
|
261
256
|
logger.debug(
|
|
262
257
|
"Processing relationship %s %s (field %s) for instance %s",
|
|
263
258
|
relationship,
|
|
@@ -347,22 +342,25 @@ class RelationshipModelBulkEditFormMixin(BulkEditForm):
|
|
|
347
342
|
def clean(self):
|
|
348
343
|
# Get any initial required relationship objects errors (i.e. non-existent required objects)
|
|
349
344
|
required_objects_errors = self.model.required_related_objects_errors(output_for="ui")
|
|
350
|
-
|
|
345
|
+
already_invalidated_keys = []
|
|
351
346
|
for field, errors in required_objects_errors.items():
|
|
352
347
|
self.add_error(None, errors)
|
|
353
|
-
|
|
354
|
-
|
|
348
|
+
# rindex() find the last occurrence of "__" which is
|
|
349
|
+
# guaranteed to be cr_{key}__source, cr_{key}__destination, or cr_{key}__peer
|
|
350
|
+
# regardless of how {key} is formatted
|
|
351
|
+
relationship_key = field[: field.rindex("__")][3:]
|
|
352
|
+
already_invalidated_keys.append(relationship_key)
|
|
355
353
|
|
|
356
354
|
required_relationships = []
|
|
357
355
|
# The following query excludes already invalidated relationships (this happened above
|
|
358
356
|
# by checking for the existence of required objects
|
|
359
357
|
# with the call to self.Meta().model.required_related_objects_errors(output_for="ui"))
|
|
360
358
|
for relationship in Relationship.objects.get_required_for_model(self.model).exclude(
|
|
361
|
-
|
|
359
|
+
key__in=already_invalidated_keys
|
|
362
360
|
):
|
|
363
361
|
required_relationships.append(
|
|
364
362
|
{
|
|
365
|
-
"
|
|
363
|
+
"key": relationship.key,
|
|
366
364
|
"required_side": RelationshipSideChoices.OPPOSITE[relationship.required_on],
|
|
367
365
|
"relationship": relationship,
|
|
368
366
|
}
|
|
@@ -371,7 +369,7 @@ class RelationshipModelBulkEditFormMixin(BulkEditForm):
|
|
|
371
369
|
# Get difference of add/remove objects for each required relationship:
|
|
372
370
|
required_relationships_to_check = []
|
|
373
371
|
for required_relationship in required_relationships:
|
|
374
|
-
required_field = f"cr_{required_relationship['
|
|
372
|
+
required_field = f"cr_{required_relationship['key']}__{required_relationship['required_side']}"
|
|
375
373
|
|
|
376
374
|
add_list = []
|
|
377
375
|
if f"add_{required_field}" in self.cleaned_data:
|
|
@@ -456,10 +454,10 @@ class RelationshipModelFormMixin(forms.ModelForm):
|
|
|
456
454
|
for relationship, queryset in relationships.items():
|
|
457
455
|
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
458
456
|
# If this model is on the "source" side of the relationship, then the field will be named
|
|
459
|
-
# cr_<
|
|
460
|
-
# If we're on the "destination" side, the field will be cr_<
|
|
461
|
-
# For a symmetric relationship, both sides are "peer", so the field will be cr_<
|
|
462
|
-
field_name = f"cr_{relationship.
|
|
457
|
+
# cr_<relationship_key>__destination since it's used to pick the destination object(s).
|
|
458
|
+
# If we're on the "destination" side, the field will be cr_<relationship_key>__source.
|
|
459
|
+
# For a symmetric relationship, both sides are "peer", so the field will be cr_<relationship_key>__peer
|
|
460
|
+
field_name = f"cr_{relationship.key}__{peer_side}"
|
|
463
461
|
self.fields[field_name] = relationship.to_form_field(side=side)
|
|
464
462
|
|
|
465
463
|
# HTML5 validation for required relationship field:
|
|
@@ -501,7 +499,7 @@ class RelationshipModelFormMixin(forms.ModelForm):
|
|
|
501
499
|
for relationship in relationships:
|
|
502
500
|
# The form field name reflects what it provides, i.e. the peer object(s) to link via this relationship.
|
|
503
501
|
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
504
|
-
field_name = f"cr_{relationship.
|
|
502
|
+
field_name = f"cr_{relationship.key}__{peer_side}"
|
|
505
503
|
|
|
506
504
|
# Is the form trying to set this field (create/update a RelationshipAssociation(s))?
|
|
507
505
|
# If not (that is, clearing the field / deleting RelationshipAssociation(s)), we don't need to check.
|
|
@@ -672,10 +670,10 @@ class RelationshipModelFilterFormMixin(forms.Form):
|
|
|
672
670
|
peer_side = RelationshipSideChoices.OPPOSITE[side]
|
|
673
671
|
|
|
674
672
|
# If this model is on the "source" side of the relationship, then the field will be named
|
|
675
|
-
# "cr_<
|
|
676
|
-
# If we're on the "destination" side, the field will be "cr_<
|
|
677
|
-
# For a symmetric relationship, both sides are "peer", so the field will be "cr_<
|
|
678
|
-
field_name = f"cr_{relationship.
|
|
673
|
+
# "cr_<relationship_key>__destination" since it's used to pick the destination object(s).
|
|
674
|
+
# If we're on the "destination" side, the field will be "cr_<relationship_key>__source".
|
|
675
|
+
# For a symmetric relationship, both sides are "peer", so the field will be "cr_<relationship_key>__peer"
|
|
676
|
+
field_name = f"cr_{relationship.key}__{peer_side}"
|
|
679
677
|
|
|
680
678
|
if field_name in self.relationships:
|
|
681
679
|
# This is a symmetric relationship that we already processed from the opposing "initial_side".
|
|
@@ -720,22 +718,6 @@ class RoleModelFilterFormMixin(forms.Form):
|
|
|
720
718
|
self.order_fields(self.field_order) # Reorder fields again
|
|
721
719
|
|
|
722
720
|
|
|
723
|
-
class RoleModelCSVFormMixin(CSVModelForm):
|
|
724
|
-
"""Mixin to add a non-required `role` choice field to CSV import forms."""
|
|
725
|
-
|
|
726
|
-
role = CSVModelChoiceField(
|
|
727
|
-
queryset=Role.objects.all(), to_field_name="name", required=False, help_text="Assigned role"
|
|
728
|
-
)
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
class RoleRequiredRoleModelCSVFormMixin(CSVModelForm):
|
|
732
|
-
"""Mixin to add a required `role` choice field to CSV import forms."""
|
|
733
|
-
|
|
734
|
-
role = CSVModelChoiceField(
|
|
735
|
-
queryset=Role.objects.all(), to_field_name="name", required=True, help_text="Assigned role"
|
|
736
|
-
)
|
|
737
|
-
|
|
738
|
-
|
|
739
721
|
class StatusModelBulkEditFormMixin(forms.Form):
|
|
740
722
|
"""Mixin to add non-required `status` choice field to forms."""
|
|
741
723
|
|
|
@@ -765,16 +747,6 @@ class StatusModelFilterFormMixin(forms.Form):
|
|
|
765
747
|
self.order_fields(self.field_order) # Reorder fields again
|
|
766
748
|
|
|
767
749
|
|
|
768
|
-
class StatusModelCSVFormMixin(CSVModelForm):
|
|
769
|
-
"""Mixin to add a required `status` choice field to CSV import forms."""
|
|
770
|
-
|
|
771
|
-
status = CSVModelChoiceField(
|
|
772
|
-
queryset=Status.objects.all(),
|
|
773
|
-
to_field_name="name",
|
|
774
|
-
help_text="Operational status",
|
|
775
|
-
)
|
|
776
|
-
|
|
777
|
-
|
|
778
750
|
class TagsBulkEditFormMixin(forms.Form):
|
|
779
751
|
def __init__(self, *args, **kwargs):
|
|
780
752
|
super().__init__(*args, **kwargs)
|