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
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
from importlib import import_module
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
import shlex
|
|
8
|
+
import subprocess
|
|
9
|
+
|
|
10
|
+
from django.apps import apps
|
|
11
|
+
from django.conf import settings
|
|
12
|
+
from django.contrib.admindocs.views import simplify_regex
|
|
13
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
14
|
+
import jinja2
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Command(BaseCommand):
|
|
18
|
+
help = "Build the user interface for the Nautobot server environment and installed Nautobot Apps."
|
|
19
|
+
|
|
20
|
+
def add_arguments(self, parser):
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"--npm-install",
|
|
23
|
+
action="store_true",
|
|
24
|
+
dest="npm_install",
|
|
25
|
+
default=False,
|
|
26
|
+
help="Install UI packages.",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--no-render-apps",
|
|
30
|
+
action="store_false",
|
|
31
|
+
dest="render_apps",
|
|
32
|
+
default=True,
|
|
33
|
+
help="Do not render Nautobot App imports.",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--no-npm-build",
|
|
37
|
+
action="store_false",
|
|
38
|
+
dest="npm_build",
|
|
39
|
+
default=True,
|
|
40
|
+
help="Do not compile UI.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def convert_django_url_regex_to_react_route_path(self, regex):
|
|
44
|
+
"""
|
|
45
|
+
Converts a regular expression object to its equivalent react router path representation.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
regex (re.Pattern): A regular expression object.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
>>> pattern = re.compile('^other-models/(?P<pk>[^/.]+)/notes/$')
|
|
52
|
+
>>> convert_django_url_regex_to_react_route_path(pattern)
|
|
53
|
+
'/other-models/:pk/notes/'
|
|
54
|
+
"""
|
|
55
|
+
pattern = str(regex.pattern)
|
|
56
|
+
path = simplify_regex(pattern).replace("<", ":").replace(">", "").replace("\\Z", "").replace("\\", "")
|
|
57
|
+
return path if path.endswith("/") else f"{path}/"
|
|
58
|
+
|
|
59
|
+
def get_app_component(self, file_path, route_name):
|
|
60
|
+
"""
|
|
61
|
+
Obtains the view component for the given route name from the index.js file of the App.
|
|
62
|
+
This locates the `route_name` in the app configuration `routes_view_components` dict and
|
|
63
|
+
returns the View Component registered for the `route_name`.
|
|
64
|
+
|
|
65
|
+
Parameters:
|
|
66
|
+
file_path (str): The path to the JavaScript file to read from.
|
|
67
|
+
route_name (str): The name of the route to extract the view component for.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
str: The view component associated with the specified route name, or None if not found.
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> get_app_component(file_path="/src/example_plugin/example_plugin/ui/index.js", route_name="example-plugin:examplemodel_list")
|
|
74
|
+
"ExampleModelListView"
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
with open(file_path, "r") as f:
|
|
78
|
+
js_content = f.read()
|
|
79
|
+
except FileNotFoundError:
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
# Construct a regular expression that matches the specified key and extracts the associated view component
|
|
83
|
+
pattern = rf'routes_view_components:\s*{{[^}}]*"{re.escape(route_name)}"\s*:\s*"([^"]+)"'
|
|
84
|
+
view_component_match = re.search(pattern, js_content)
|
|
85
|
+
return view_component_match[1] if view_component_match else None
|
|
86
|
+
|
|
87
|
+
def render_routes_imports(self, app_base_path, app_name, app_config):
|
|
88
|
+
"""
|
|
89
|
+
Renders the imports for the React Router components of the App's URLs.
|
|
90
|
+
|
|
91
|
+
This method inspects the `urlpatterns` list of the `urls.py` module of the App and generates
|
|
92
|
+
a list of dictionaries representing the React Router components and their corresponding URL paths.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
app_base_path (Path): The base path of the Django app.
|
|
96
|
+
app_name (str): The name of the Django app.
|
|
97
|
+
app_config (AppConfig): The configuration object of the Django app.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
List[Dict[str, str]]: A list of dictionaries representing the React Router components and their
|
|
101
|
+
corresponding URL paths.
|
|
102
|
+
Each dictionary has the following keys:
|
|
103
|
+
- "path": The URL path pattern.
|
|
104
|
+
- "component": The React component associated with the URL path.
|
|
105
|
+
|
|
106
|
+
Example:
|
|
107
|
+
>>> render_routes_imports("/src/example_plugin/", "example_plugin", <AppConfig instance>)
|
|
108
|
+
[
|
|
109
|
+
{ "path": "example-plugin/", "component": "HomeView"},
|
|
110
|
+
{ "path": "example-plugin/config/", "component": "ConfigView"},
|
|
111
|
+
]
|
|
112
|
+
"""
|
|
113
|
+
data = []
|
|
114
|
+
try:
|
|
115
|
+
module = import_module(f"{app_name}.urls")
|
|
116
|
+
base_url = app_config.base_url or app_config.label
|
|
117
|
+
for urlpattern in module.urlpatterns:
|
|
118
|
+
if component := self.get_app_component(
|
|
119
|
+
app_base_path / "ui/index.js",
|
|
120
|
+
f"{base_url}:{urlpattern.name}",
|
|
121
|
+
):
|
|
122
|
+
path_regex = urlpattern.pattern.regex
|
|
123
|
+
url_path = self.convert_django_url_regex_to_react_route_path(path_regex)
|
|
124
|
+
data.append({"path": base_url + url_path, "component": component})
|
|
125
|
+
return data
|
|
126
|
+
except (AttributeError, ModuleNotFoundError):
|
|
127
|
+
# If an app does not include its url.py file or urls.py does not include a urlpatterns, skip.
|
|
128
|
+
return data
|
|
129
|
+
|
|
130
|
+
def render_app_imports(self):
|
|
131
|
+
"""Render `app_imports.js`, update `jsconfig.json` to map to the path for each and render `app_routes.json` to register all app routes."""
|
|
132
|
+
self.stdout.write(self.style.WARNING(">>> Rendering Nautobot App imports..."))
|
|
133
|
+
|
|
134
|
+
ui_dir = settings.NAUTOBOT_UI_DIR
|
|
135
|
+
|
|
136
|
+
router_file_path = Path(ui_dir, "src", "router.js")
|
|
137
|
+
app_routes_file_path = Path(ui_dir, "src", "app_routes.json")
|
|
138
|
+
jsconfig_file_path = Path(ui_dir, "jsconfig.paths.json")
|
|
139
|
+
jsconfig_base_file_path = Path(ui_dir, "jsconfig-base.json")
|
|
140
|
+
app_routes = {}
|
|
141
|
+
|
|
142
|
+
with open(jsconfig_base_file_path, "r", encoding="utf-8") as base_config_file:
|
|
143
|
+
jsconfig = json.load(base_config_file)
|
|
144
|
+
|
|
145
|
+
# We're going to modify this list if apps don't have a `ui` directory.
|
|
146
|
+
enabled_apps = copy.copy(settings.PLUGINS)
|
|
147
|
+
|
|
148
|
+
for app_class_path in settings.PLUGINS:
|
|
149
|
+
app_name = app_class_path.split(".")[-1]
|
|
150
|
+
app_config = apps.get_app_config(app_name)
|
|
151
|
+
abs_app_path = Path(app_config.path).resolve()
|
|
152
|
+
abs_app_ui_path = abs_app_path / "ui"
|
|
153
|
+
app_path = Path(os.path.relpath(abs_app_path, ui_dir))
|
|
154
|
+
app_ui_path = app_path / "ui"
|
|
155
|
+
if app_routes_imports := self.render_routes_imports(abs_app_path, app_name, app_config):
|
|
156
|
+
app_routes[app_name] = app_routes_imports
|
|
157
|
+
|
|
158
|
+
# Assert that an App has a UI folder.
|
|
159
|
+
if not abs_app_ui_path.exists():
|
|
160
|
+
self.stdout.write(self.style.ERROR(f"- App {app_name!r} does not publish a UI; Skipping..."))
|
|
161
|
+
enabled_apps.remove(app_class_path)
|
|
162
|
+
continue
|
|
163
|
+
self.stdout.write(self.style.SUCCESS(f"- App {app_name!r} imported"))
|
|
164
|
+
|
|
165
|
+
jsconfig["compilerOptions"]["paths"][f"@{app_name}/*"] = [f"{app_ui_path}/*"]
|
|
166
|
+
|
|
167
|
+
with open(jsconfig_file_path, "w", encoding="utf-8") as generated_config_file:
|
|
168
|
+
json.dump(jsconfig, generated_config_file, indent=4)
|
|
169
|
+
|
|
170
|
+
with open(app_routes_file_path, "w", encoding="utf-8") as app_routes_file:
|
|
171
|
+
json.dump(app_routes, app_routes_file, indent=4)
|
|
172
|
+
|
|
173
|
+
app_imports_final_file_path = Path(ui_dir, "src", "app_imports.js")
|
|
174
|
+
environment = jinja2.sandbox.SandboxedEnvironment(loader=jinja2.FileSystemLoader(ui_dir))
|
|
175
|
+
template = environment.get_template("app_imports.js.j2")
|
|
176
|
+
content = template.render(apps=enabled_apps)
|
|
177
|
+
|
|
178
|
+
with open(app_imports_final_file_path, "w", encoding="utf-8") as generated_import_file:
|
|
179
|
+
generated_import_file.write(content)
|
|
180
|
+
|
|
181
|
+
# Touch the router to attempt to trigger a server reload.
|
|
182
|
+
Path(router_file_path).touch()
|
|
183
|
+
|
|
184
|
+
def run_command(self, command, message, cwd=settings.NAUTOBOT_UI_DIR):
|
|
185
|
+
"""
|
|
186
|
+
Run a `command`, displaying `message` and exit. This splits it for you and runs it.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
command (str): The command to execute
|
|
190
|
+
message (str): Message to display when command is executed
|
|
191
|
+
"""
|
|
192
|
+
self.stdout.write(self.style.WARNING(message))
|
|
193
|
+
self.stdout.write(f"Running '{command}' in '{cwd}'...")
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
result = subprocess.run(
|
|
197
|
+
shlex.split(command),
|
|
198
|
+
check=False,
|
|
199
|
+
cwd=cwd,
|
|
200
|
+
env={**os.environ.copy()},
|
|
201
|
+
encoding="utf-8",
|
|
202
|
+
capture_output=True,
|
|
203
|
+
)
|
|
204
|
+
except FileNotFoundError as err:
|
|
205
|
+
raise CommandError(f"'{command}' failed with error: {err}")
|
|
206
|
+
|
|
207
|
+
if result.returncode:
|
|
208
|
+
self.stdout.write(self.style.NOTICE(result.stdout))
|
|
209
|
+
self.stderr.write(self.style.ERROR(result.stderr))
|
|
210
|
+
raise CommandError(f"'{command}' failed with exit code {result.returncode}")
|
|
211
|
+
|
|
212
|
+
def handle(self, *args, **options):
|
|
213
|
+
verbosity = options["verbosity"]
|
|
214
|
+
|
|
215
|
+
# NAUTOBOT_DEBUG is also set here so that `console.log` isn't suppressed when we want
|
|
216
|
+
# verbosity.
|
|
217
|
+
if verbosity > 1:
|
|
218
|
+
os.environ["NAUTOBOT_DEBUG"] = "1"
|
|
219
|
+
|
|
220
|
+
verbosity_map = {
|
|
221
|
+
0: "silent",
|
|
222
|
+
1: "notice",
|
|
223
|
+
2: "info",
|
|
224
|
+
3: "silly", # MAXIMUM OVERDEBUG
|
|
225
|
+
}
|
|
226
|
+
loglevel = f"--loglevel {verbosity_map[verbosity]}"
|
|
227
|
+
|
|
228
|
+
if not os.path.exists(Path(settings.NAUTOBOT_UI_DIR)):
|
|
229
|
+
os.makedirs(Path(settings.NAUTOBOT_UI_DIR))
|
|
230
|
+
|
|
231
|
+
self.run_command(
|
|
232
|
+
f"cp -r {Path(settings.BASE_DIR, 'ui')}/. {Path(settings.NAUTOBOT_UI_DIR)}",
|
|
233
|
+
">>> Copying UI source files...",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Generate `app_imports.js`
|
|
237
|
+
if options["render_apps"]:
|
|
238
|
+
self.render_app_imports()
|
|
239
|
+
|
|
240
|
+
# Run `npm install` and keep it silent by default.
|
|
241
|
+
if options["npm_install"]:
|
|
242
|
+
args = f"install {loglevel} --no-progress"
|
|
243
|
+
self.run_command(f"npm {args}", ">>> Installing Nautobot UI dependencies...")
|
|
244
|
+
|
|
245
|
+
# Run `npm build` and keep it silent by default.
|
|
246
|
+
if options["npm_build"]:
|
|
247
|
+
args = f"run build {loglevel}"
|
|
248
|
+
self.run_command(f"npm {args}", ">>> Compiling Nautobot UI packages...")
|
|
249
|
+
self.run_command(
|
|
250
|
+
f"cp {Path(settings.NAUTOBOT_UI_DIR, 'build' ,'asset-manifest.json')} "
|
|
251
|
+
f"{Path(settings.NAUTOBOT_UI_DIR, 'build' , 'static', 'asset-manifest.json')}",
|
|
252
|
+
">>> Copying built manifest...",
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
self.stdout.write(self.style.SUCCESS(">>> Nautobot UI build complete! 🎉"))
|
|
@@ -185,15 +185,16 @@ Type 'yes' to continue, or 'no' to cancel: """
|
|
|
185
185
|
call_command("flush", "--no-input")
|
|
186
186
|
|
|
187
187
|
if options["cache_test_fixtures"] and os.path.exists(options["fixture_file"]):
|
|
188
|
+
self.stdout.write(self.style.WARNING(f"Loading factory data from file {options['fixture_file']}"))
|
|
188
189
|
call_command("loaddata", options["fixture_file"])
|
|
189
190
|
else:
|
|
190
191
|
self._generate_factory_data(options["seed"])
|
|
191
192
|
|
|
192
193
|
if options["cache_test_fixtures"]:
|
|
194
|
+
self.stdout.write(self.style.WARNING(f"Saving factory data to file {options['fixture_file']}"))
|
|
195
|
+
|
|
193
196
|
call_command(
|
|
194
197
|
"dumpdata",
|
|
195
|
-
"--natural-foreign",
|
|
196
|
-
"--natural-primary",
|
|
197
198
|
indent=2,
|
|
198
199
|
format="json",
|
|
199
200
|
exclude=["contenttypes", "auth.permission", "extras.job", "extras.customfield"],
|
|
@@ -11,10 +11,10 @@ This will run the following management commands with default settings, in order:
|
|
|
11
11
|
|
|
12
12
|
- migrate
|
|
13
13
|
- trace_paths
|
|
14
|
+
- build_ui --npm-install
|
|
14
15
|
- collectstatic
|
|
15
16
|
- remove_stale_contenttypes
|
|
16
17
|
- clearsessions
|
|
17
|
-
- invalidate all
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
|
|
@@ -28,6 +28,13 @@ class Command(BaseCommand):
|
|
|
28
28
|
return parser
|
|
29
29
|
|
|
30
30
|
def add_arguments(self, parser):
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--no-build-ui",
|
|
33
|
+
action="store_false",
|
|
34
|
+
dest="build_ui",
|
|
35
|
+
default=True,
|
|
36
|
+
help="Do not automatically build the user interface.",
|
|
37
|
+
)
|
|
31
38
|
parser.add_argument(
|
|
32
39
|
"--no-clearsessions",
|
|
33
40
|
action="store_false",
|
|
@@ -42,13 +49,6 @@ class Command(BaseCommand):
|
|
|
42
49
|
default=True,
|
|
43
50
|
help="Do not automatically collect static files into a single location.",
|
|
44
51
|
)
|
|
45
|
-
parser.add_argument(
|
|
46
|
-
"--no-invalidate-all",
|
|
47
|
-
action="store_false",
|
|
48
|
-
dest="invalidate_all",
|
|
49
|
-
default=True,
|
|
50
|
-
help="Do not automatically invalidate cache for entire application.",
|
|
51
|
-
)
|
|
52
52
|
parser.add_argument(
|
|
53
53
|
"--no-migrate",
|
|
54
54
|
action="store_false",
|
|
@@ -74,41 +74,41 @@ class Command(BaseCommand):
|
|
|
74
74
|
def handle(self, *args, **options):
|
|
75
75
|
# Run migrate
|
|
76
76
|
if options.get("migrate"):
|
|
77
|
-
|
|
77
|
+
self.stdout.write("Performing database migrations...")
|
|
78
78
|
call_command(
|
|
79
79
|
"migrate",
|
|
80
80
|
interactive=False,
|
|
81
81
|
traceback=options["traceback"],
|
|
82
82
|
verbosity=options["verbosity"],
|
|
83
83
|
)
|
|
84
|
-
|
|
84
|
+
self.stdout.write()
|
|
85
85
|
|
|
86
86
|
# Run trace_paths
|
|
87
87
|
if options.get("trace_paths"):
|
|
88
|
-
|
|
88
|
+
self.stdout.write("Generating cable paths...")
|
|
89
89
|
call_command("trace_paths", no_input=True)
|
|
90
|
-
|
|
90
|
+
self.stdout.write()
|
|
91
|
+
|
|
92
|
+
# Run build
|
|
93
|
+
if options.get("build_ui"):
|
|
94
|
+
self.stdout.write("Building user interface...")
|
|
95
|
+
call_command("build_ui", npm_install=True)
|
|
96
|
+
self.stdout.write()
|
|
91
97
|
|
|
92
98
|
# Run collectstatic
|
|
93
99
|
if options.get("collectstatic"):
|
|
94
|
-
|
|
100
|
+
self.stdout.write("Collecting static files...")
|
|
95
101
|
call_command("collectstatic", interactive=False)
|
|
96
|
-
|
|
102
|
+
self.stdout.write()
|
|
97
103
|
|
|
98
104
|
# Run remove_stale_contenttypes
|
|
99
105
|
if options.get("remove_stale_contenttypes"):
|
|
100
|
-
|
|
106
|
+
self.stdout.write("Removing stale content types...")
|
|
101
107
|
call_command("remove_stale_contenttypes", interactive=False)
|
|
102
|
-
|
|
108
|
+
self.stdout.write()
|
|
103
109
|
|
|
104
110
|
# Run clearsessions
|
|
105
111
|
if options.get("clearsessions"):
|
|
106
|
-
|
|
112
|
+
self.stdout.write("Removing expired sessions...")
|
|
107
113
|
call_command("clearsessions")
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# Run invalidate all
|
|
111
|
-
if options.get("invalidate_all"):
|
|
112
|
-
print("Invalidating cache...")
|
|
113
|
-
call_command("invalidate", "all")
|
|
114
|
-
print()
|
|
114
|
+
self.stdout.write()
|
nautobot/core/models/__init__.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
|
+
from django.urls import NoReverseMatch, reverse
|
|
4
5
|
from django.utils.functional import classproperty
|
|
5
6
|
|
|
6
7
|
from nautobot.core.models.managers import BaseManager
|
|
7
8
|
from nautobot.core.models.querysets import RestrictedQuerySet
|
|
8
9
|
from nautobot.core.models.utils import construct_natural_key_slug
|
|
10
|
+
from nautobot.core.utils.lookup import get_route_for_model
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class BaseModel(models.Model):
|
|
@@ -37,6 +39,29 @@ class BaseModel(models.Model):
|
|
|
37
39
|
"""
|
|
38
40
|
return not self._state.adding
|
|
39
41
|
|
|
42
|
+
def get_absolute_url(self, api=False):
|
|
43
|
+
"""
|
|
44
|
+
Return the canonical URL for this object in either the UI or the REST API.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Iterate the pk-like fields and try to get a URL, or return None.
|
|
48
|
+
fields = ["pk", "slug"] # TODO: Eventually all PKs
|
|
49
|
+
actions = ["retrieve", "detail", ""] # TODO: Eventually all retrieve
|
|
50
|
+
|
|
51
|
+
for field in fields:
|
|
52
|
+
if not hasattr(self, field):
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
for action in actions:
|
|
56
|
+
route = get_route_for_model(self, action, api=api)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
return reverse(route, kwargs={field: getattr(self, field)})
|
|
60
|
+
except NoReverseMatch:
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
return AttributeError(f"Cannot find a URL for {self} ({self._meta.app_label}.{self._meta.model_name})")
|
|
64
|
+
|
|
40
65
|
class Meta:
|
|
41
66
|
abstract = True
|
|
42
67
|
|
|
@@ -48,7 +73,7 @@ class BaseModel(models.Model):
|
|
|
48
73
|
which in effect enforces model validation prior to saving the instance, without having
|
|
49
74
|
to manually make these calls seperately. This is a slight departure from Django norms,
|
|
50
75
|
but is intended to offer an optional, simplified interface for performing this common
|
|
51
|
-
workflow. The intended use is for user defined Jobs
|
|
76
|
+
workflow. The intended use is for user defined Jobs run via the `nautobot-server nbshell`
|
|
52
77
|
command.
|
|
53
78
|
"""
|
|
54
79
|
self.full_clean()
|
nautobot/core/models/fields.py
CHANGED
|
@@ -8,9 +8,11 @@ from django.db import models
|
|
|
8
8
|
from django.utils.text import slugify
|
|
9
9
|
from django_extensions.db.fields import AutoSlugField as _AutoSlugField
|
|
10
10
|
from netaddr import AddrFormatError, EUI, mac_unix_expanded
|
|
11
|
+
from taggit.managers import TaggableManager
|
|
11
12
|
|
|
12
13
|
from nautobot.core.forms import fields, widgets
|
|
13
14
|
from nautobot.core.models import ordering
|
|
15
|
+
from nautobot.core.models.managers import TagsManager
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class mac_unix_expanded_uppercase(mac_unix_expanded):
|
|
@@ -184,10 +186,6 @@ class ForeignKeyLimitedByContentTypes(ForeignKeyWithAutoRelatedName):
|
|
|
184
186
|
- the content-type to which the field is attached (e.g. `dcim.device`)
|
|
185
187
|
"""
|
|
186
188
|
|
|
187
|
-
def __init__(self, **kwargs):
|
|
188
|
-
kwargs.setdefault("null", True)
|
|
189
|
-
super().__init__(**kwargs)
|
|
190
|
-
|
|
191
189
|
def get_limit_choices_to(self):
|
|
192
190
|
return {"content_types": ContentType.objects.get_for_model(self.model)}
|
|
193
191
|
|
|
@@ -323,7 +321,7 @@ class JSONArrayField(models.JSONField):
|
|
|
323
321
|
# Assume we're deserializing
|
|
324
322
|
vals = json.loads(value)
|
|
325
323
|
value = [self.base_field.to_python(val) for val in vals]
|
|
326
|
-
except json.JSONDecodeError as e:
|
|
324
|
+
except (TypeError, json.JSONDecodeError) as e:
|
|
327
325
|
raise exceptions.ValidationError(e)
|
|
328
326
|
return value
|
|
329
327
|
|
|
@@ -370,3 +368,24 @@ class JSONArrayField(models.JSONField):
|
|
|
370
368
|
**kwargs,
|
|
371
369
|
}
|
|
372
370
|
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class TagsField(TaggableManager):
|
|
374
|
+
"""Override FormField method on taggit.managers.TaggableManager to match the Nautobot UI."""
|
|
375
|
+
|
|
376
|
+
def __init__(self, *args, **kwargs):
|
|
377
|
+
from nautobot.extras.models.tags import TaggedItem
|
|
378
|
+
|
|
379
|
+
kwargs.setdefault("through", TaggedItem)
|
|
380
|
+
kwargs.setdefault("manager", TagsManager)
|
|
381
|
+
kwargs.setdefault("ordering", ["name"])
|
|
382
|
+
super().__init__(*args, **kwargs)
|
|
383
|
+
|
|
384
|
+
def formfield(self, form_class=fields.DynamicModelMultipleChoiceField, **kwargs):
|
|
385
|
+
from nautobot.extras.models.tags import Tag
|
|
386
|
+
|
|
387
|
+
queryset = Tag.objects.get_for_model(self.model)
|
|
388
|
+
kwargs.setdefault("queryset", queryset)
|
|
389
|
+
kwargs.setdefault("required", False)
|
|
390
|
+
kwargs.setdefault("query_params", {"content_types": self.model._meta.label_lower})
|
|
391
|
+
return super().formfield(form_class=form_class, **kwargs)
|
nautobot/core/models/generics.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from taggit.managers import TaggableManager, _TaggableManager
|
|
4
|
-
|
|
5
3
|
from nautobot.extras.models.change_logging import ChangeLoggedModel
|
|
6
4
|
from nautobot.extras.models.customfields import CustomFieldModel
|
|
7
5
|
from nautobot.extras.models.mixins import DynamicGroupMixin, NotesMixin
|
|
8
6
|
from nautobot.extras.models.relationships import RelationshipModel
|
|
9
|
-
from nautobot.extras.models.tags import TaggedItem
|
|
10
7
|
from nautobot.core.models import BaseModel
|
|
8
|
+
from nautobot.core.models.fields import TagsField
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
logger = logging.getLogger(__name__)
|
|
@@ -30,44 +28,6 @@ class OrganizationalModel(
|
|
|
30
28
|
abstract = True
|
|
31
29
|
|
|
32
30
|
|
|
33
|
-
# 2.0 TODO: remove this, force migration to the newer django-taggit API.
|
|
34
|
-
class _NautobotTaggableManager(_TaggableManager):
|
|
35
|
-
"""Extend _TaggableManager to work around a breaking API change between django-taggit 1.x and 2.x.
|
|
36
|
-
|
|
37
|
-
This is a bit confusing, as there's also a related `TaggableManager` class as well.
|
|
38
|
-
`TaggableManager` is the *model field* (subclass of `models.fields.related.RelatedField`),
|
|
39
|
-
while `_TaggableManager` is the *associated manager* (subclass of `models.Manager`).
|
|
40
|
-
|
|
41
|
-
For `TaggableManager`, we chose to monkey-patch rather than subclass to override its `formfield` method;
|
|
42
|
-
replacing it with a subclass would create database migrations for every PrimaryModel with a `tags` field.
|
|
43
|
-
In 2.0 we'll want to bite the bullet and make the cutover (#1633).
|
|
44
|
-
|
|
45
|
-
For `_TaggableManager`, we can subclass rather than monkey-patching because replacing it *doesn't* require
|
|
46
|
-
a database migration, and this is cleaner than a monkey-patch.
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
def set(self, *tags, through_defaults=None, **kwargs):
|
|
50
|
-
"""
|
|
51
|
-
Patch model.tags.set() to be backwards-compatible with django-taggit 1.x and forward-compatible with later.
|
|
52
|
-
|
|
53
|
-
Both of these approaches are supported:
|
|
54
|
-
|
|
55
|
-
- tags.set("tag 1", "tag 2") # django-taggit 1.x
|
|
56
|
-
- tags.set(["tag 1", "tag 2"]) # django-taggit 2.x and later
|
|
57
|
-
"""
|
|
58
|
-
if len(tags) == 1 and not isinstance(tags[0], (self.through.tag_model(), str)):
|
|
59
|
-
# taggit 2.x+ style, i.e. `set([tag, tag, tag])`
|
|
60
|
-
tags = tags[0]
|
|
61
|
-
else:
|
|
62
|
-
# taggit 1.x style, i.e. `set(tag, tag, tag)`
|
|
63
|
-
# Note: logger.warning() only supports a `stacklevel` parameter in Python 3.8 and later
|
|
64
|
-
tags_unpacked = ", ".join([repr(tag) for tag in tags])
|
|
65
|
-
tags_list = list(tags)
|
|
66
|
-
message = "Deprecated `tags.set(%s)` was called, please change to `tags.set(%s)` instead"
|
|
67
|
-
logger.warning(message, tags_unpacked, tags_list, stacklevel=2)
|
|
68
|
-
return super().set(tags, through_defaults=through_defaults, **kwargs)
|
|
69
|
-
|
|
70
|
-
|
|
71
31
|
class PrimaryModel(BaseModel, ChangeLoggedModel, CustomFieldModel, RelationshipModel, DynamicGroupMixin, NotesMixin):
|
|
72
32
|
"""
|
|
73
33
|
Base abstract model for all primary models.
|
|
@@ -78,7 +38,7 @@ class PrimaryModel(BaseModel, ChangeLoggedModel, CustomFieldModel, RelationshipM
|
|
|
78
38
|
tangible or logical resources on the network, or within the organization.
|
|
79
39
|
"""
|
|
80
40
|
|
|
81
|
-
tags =
|
|
41
|
+
tags = TagsField()
|
|
82
42
|
|
|
83
43
|
class Meta:
|
|
84
44
|
abstract = True
|
nautobot/core/models/managers.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from django.db.models import Manager
|
|
2
|
+
from taggit.managers import _TaggableManager
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class BaseManager(Manager):
|
|
@@ -43,3 +44,7 @@ class BaseManager(Manager):
|
|
|
43
44
|
raise self.model.DoesNotExist() from exc
|
|
44
45
|
|
|
45
46
|
return self.get(**kwargs)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TagsManager(_TaggableManager, BaseManager):
|
|
50
|
+
"""Manager class for model 'tags' fields."""
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from django.contrib.contenttypes.models import ContentType
|
|
2
2
|
from django.db import models
|
|
3
3
|
from django.db.models import Q
|
|
4
|
-
from django.urls import reverse
|
|
5
4
|
|
|
6
5
|
from nautobot.core.choices import ColorChoices
|
|
7
6
|
from nautobot.core.models import BaseManager, BaseModel
|
|
@@ -66,7 +65,6 @@ class NameColorContentTypesModel(
|
|
|
66
65
|
|
|
67
66
|
objects = BaseManager.from_queryset(ContentTypeRelatedQuerySet)()
|
|
68
67
|
|
|
69
|
-
csv_headers = ["name", "color", "content_types", "description"]
|
|
70
68
|
clone_fields = ["color", "content_types"]
|
|
71
69
|
|
|
72
70
|
class Meta:
|
|
@@ -76,17 +74,5 @@ class NameColorContentTypesModel(
|
|
|
76
74
|
def __str__(self):
|
|
77
75
|
return self.name
|
|
78
76
|
|
|
79
|
-
def get_absolute_url(self):
|
|
80
|
-
ct = f"{self._meta.app_label}:{self._meta.model_name}"
|
|
81
|
-
return reverse(ct, args=[self.pk])
|
|
82
|
-
|
|
83
77
|
def get_content_types(self):
|
|
84
78
|
return ",".join(f"{ct.app_label}.{ct.model}" for ct in self.content_types.all())
|
|
85
|
-
|
|
86
|
-
def to_csv(self):
|
|
87
|
-
return (
|
|
88
|
-
self.name,
|
|
89
|
-
self.color,
|
|
90
|
-
self.get_content_types(),
|
|
91
|
-
self.description,
|
|
92
|
-
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from django.core.cache import cache
|
|
2
|
+
|
|
1
3
|
from tree_queries.models import TreeNode
|
|
2
4
|
from tree_queries.query import TreeManager as TreeManager_
|
|
3
5
|
from tree_queries.query import TreeQuerySet as TreeQuerySet_
|
|
@@ -41,16 +43,24 @@ class TreeModel(TreeNode):
|
|
|
41
43
|
|
|
42
44
|
@property
|
|
43
45
|
def display(self):
|
|
44
|
-
"""
|
|
46
|
+
"""
|
|
47
|
+
By default, TreeModels display their full ancestry for clarity.
|
|
48
|
+
|
|
49
|
+
As this is an expensive thing to calculate, we cache it for a few seconds in the case of repeated lookups.
|
|
50
|
+
"""
|
|
45
51
|
if not hasattr(self, "name"):
|
|
46
52
|
raise NotImplementedError("default TreeModel.display implementation requires a `name` attribute!")
|
|
47
|
-
|
|
53
|
+
cache_key = f"{self.__class__.__name__}.{self.id}.display"
|
|
54
|
+
display_str = cache.get(cache_key, "")
|
|
55
|
+
if display_str:
|
|
56
|
+
return display_str
|
|
48
57
|
try:
|
|
49
|
-
|
|
50
|
-
display_str
|
|
58
|
+
if self.parent is not None:
|
|
59
|
+
display_str = self.parent.display + " → "
|
|
51
60
|
except self.DoesNotExist:
|
|
52
61
|
# Expected to occur at times during bulk-delete operations
|
|
53
62
|
pass
|
|
54
63
|
finally:
|
|
55
64
|
display_str += self.name
|
|
65
|
+
cache.set(cache_key, display_str, 5)
|
|
56
66
|
return display_str # pylint: disable=lost-exception
|
nautobot/core/models/utils.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from itertools import count, groupby
|
|
2
1
|
import json
|
|
2
|
+
|
|
3
|
+
from itertools import count, groupby
|
|
3
4
|
from urllib.parse import quote_plus, unquote_plus
|
|
4
5
|
|
|
5
6
|
from django.apps import apps
|
|
6
7
|
from django.core.exceptions import FieldDoesNotExist
|
|
7
8
|
from django.core.serializers import serialize
|
|
8
9
|
from django.utils.tree import Node
|
|
9
|
-
from taggit.managers import _TaggableManager
|
|
10
10
|
|
|
11
11
|
from nautobot.core.models.constants import NATURAL_KEY_SLUG_SEPARATOR
|
|
12
12
|
|
|
@@ -35,10 +35,9 @@ def is_taggable(obj):
|
|
|
35
35
|
"""
|
|
36
36
|
Return True if the instance can have Tags assigned to it; False otherwise.
|
|
37
37
|
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return False
|
|
38
|
+
from nautobot.core.models.managers import TagsManager
|
|
39
|
+
|
|
40
|
+
return hasattr(obj, "tags") and isinstance(obj.tags, TagsManager)
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
def pretty_print_query(query):
|