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
nautobot/dcim/forms.py
CHANGED
|
@@ -2,11 +2,7 @@ import re
|
|
|
2
2
|
|
|
3
3
|
from django import forms
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
|
-
from django.contrib.contenttypes.models import ContentType
|
|
6
|
-
from django.contrib.postgres.forms.array import SimpleArrayField
|
|
7
|
-
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
8
5
|
from django.db.models import Q
|
|
9
|
-
from django.utils.safestring import mark_safe
|
|
10
6
|
from timezone_field import TimeZoneFormField
|
|
11
7
|
|
|
12
8
|
from nautobot.circuits.models import Circuit, CircuitTermination, Provider
|
|
@@ -18,10 +14,6 @@ from nautobot.core.forms import (
|
|
|
18
14
|
BulkEditNullBooleanSelect,
|
|
19
15
|
ColorSelect,
|
|
20
16
|
CommentField,
|
|
21
|
-
CSVChoiceField,
|
|
22
|
-
CSVContentTypeField,
|
|
23
|
-
CSVModelChoiceField,
|
|
24
|
-
CSVMultipleContentTypeField,
|
|
25
17
|
DynamicModelChoiceField,
|
|
26
18
|
DynamicModelMultipleChoiceField,
|
|
27
19
|
ExpandableNameField,
|
|
@@ -38,7 +30,6 @@ from nautobot.core.forms import (
|
|
|
38
30
|
from nautobot.core.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
|
|
39
31
|
from nautobot.dcim.form_mixins import (
|
|
40
32
|
LocatableModelBulkEditFormMixin,
|
|
41
|
-
LocatableModelCSVFormMixin,
|
|
42
33
|
LocatableModelFilterFormMixin,
|
|
43
34
|
LocatableModelFormMixin,
|
|
44
35
|
)
|
|
@@ -52,11 +43,8 @@ from nautobot.extras.forms import (
|
|
|
52
43
|
LocalContextModelForm,
|
|
53
44
|
LocalContextModelBulkEditForm,
|
|
54
45
|
RoleModelBulkEditFormMixin,
|
|
55
|
-
RoleModelCSVFormMixin,
|
|
56
46
|
RoleModelFilterFormMixin,
|
|
57
|
-
RoleRequiredRoleModelCSVFormMixin,
|
|
58
47
|
StatusModelBulkEditFormMixin,
|
|
59
|
-
StatusModelCSVFormMixin,
|
|
60
48
|
StatusModelFilterFormMixin,
|
|
61
49
|
TagsBulkEditFormMixin,
|
|
62
50
|
)
|
|
@@ -87,10 +75,8 @@ from .choices import (
|
|
|
87
75
|
SubdeviceRoleChoices,
|
|
88
76
|
)
|
|
89
77
|
from .constants import (
|
|
90
|
-
CABLE_TERMINATION_MODELS,
|
|
91
78
|
INTERFACE_MTU_MAX,
|
|
92
79
|
INTERFACE_MTU_MIN,
|
|
93
|
-
NONCONNECTABLE_IFACE_TYPES,
|
|
94
80
|
REARPORT_POSITIONS_MAX,
|
|
95
81
|
REARPORT_POSITIONS_MIN,
|
|
96
82
|
)
|
|
@@ -258,29 +244,6 @@ class LocationTypeForm(NautobotModelForm):
|
|
|
258
244
|
fields = ("parent", "name", "slug", "description", "nestable", "content_types")
|
|
259
245
|
|
|
260
246
|
|
|
261
|
-
class LocationTypeCSVForm(CustomFieldModelCSVForm):
|
|
262
|
-
parent = CSVModelChoiceField(
|
|
263
|
-
queryset=LocationType.objects.all(),
|
|
264
|
-
required=False,
|
|
265
|
-
to_field_name="name",
|
|
266
|
-
help_text="Name of parent location type",
|
|
267
|
-
)
|
|
268
|
-
content_types = CSVMultipleContentTypeField(
|
|
269
|
-
feature="locations",
|
|
270
|
-
required=False,
|
|
271
|
-
choices_as_strings=True,
|
|
272
|
-
help_text=mark_safe(
|
|
273
|
-
"The object types to which this status applies. Multiple values "
|
|
274
|
-
"must be comma-separated and wrapped in double quotes. (e.g. "
|
|
275
|
-
'<code>"dcim.device,dcim.rack"</code>)'
|
|
276
|
-
),
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
class Meta:
|
|
280
|
-
model = LocationType
|
|
281
|
-
fields = LocationType.csv_headers
|
|
282
|
-
|
|
283
|
-
|
|
284
247
|
class LocationTypeFilterForm(NautobotFilterForm):
|
|
285
248
|
model = LocationType
|
|
286
249
|
q = forms.CharField(required=False, label="Search")
|
|
@@ -378,35 +341,6 @@ class LocationBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin,
|
|
|
378
341
|
]
|
|
379
342
|
|
|
380
343
|
|
|
381
|
-
class LocationCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
382
|
-
location_type = CSVModelChoiceField(
|
|
383
|
-
queryset=LocationType.objects.all(),
|
|
384
|
-
to_field_name="name",
|
|
385
|
-
help_text="Location type",
|
|
386
|
-
)
|
|
387
|
-
parent = CSVModelChoiceField(
|
|
388
|
-
queryset=Location.objects.all(),
|
|
389
|
-
required=False,
|
|
390
|
-
to_field_name="name",
|
|
391
|
-
help_text="Parent location",
|
|
392
|
-
)
|
|
393
|
-
tenant = CSVModelChoiceField(
|
|
394
|
-
queryset=Tenant.objects.all(),
|
|
395
|
-
required=False,
|
|
396
|
-
to_field_name="name",
|
|
397
|
-
help_text="Assigned tenant",
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
class Meta:
|
|
401
|
-
model = Location
|
|
402
|
-
fields = Location.csv_headers
|
|
403
|
-
help_texts = {
|
|
404
|
-
"time_zone": mark_safe(
|
|
405
|
-
'Time zone (<a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">available options</a>)'
|
|
406
|
-
)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
344
|
class LocationFilterForm(NautobotFilterForm, StatusModelFilterFormMixin, TenancyFilterForm):
|
|
411
345
|
model = Location
|
|
412
346
|
field_order = ["q", "location_type", "parent", "subtree", "status", "tenant_group", "tenant", "tag"]
|
|
@@ -444,22 +378,6 @@ class RackGroupForm(LocatableModelFormMixin, NautobotModelForm):
|
|
|
444
378
|
)
|
|
445
379
|
|
|
446
380
|
|
|
447
|
-
class RackGroupCSVForm(LocatableModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
448
|
-
parent = CSVModelChoiceField(
|
|
449
|
-
queryset=RackGroup.objects.all(),
|
|
450
|
-
required=False,
|
|
451
|
-
to_field_name="name",
|
|
452
|
-
help_text="Parent rack group",
|
|
453
|
-
error_messages={
|
|
454
|
-
"invalid_choice": "Rack group not found.",
|
|
455
|
-
},
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
class Meta:
|
|
459
|
-
model = RackGroup
|
|
460
|
-
fields = RackGroup.csv_headers
|
|
461
|
-
|
|
462
|
-
|
|
463
381
|
class RackGroupFilterForm(NautobotFilterForm, LocatableModelFilterFormMixin):
|
|
464
382
|
model = RackGroup
|
|
465
383
|
parent = DynamicModelMultipleChoiceField(
|
|
@@ -519,35 +437,6 @@ class RackForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
|
|
|
519
437
|
}
|
|
520
438
|
|
|
521
439
|
|
|
522
|
-
class RackCSVForm(LocatableModelCSVFormMixin, StatusModelCSVFormMixin, RoleModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
523
|
-
rack_group = CSVModelChoiceField(queryset=RackGroup.objects.all(), required=False, to_field_name="name")
|
|
524
|
-
tenant = CSVModelChoiceField(
|
|
525
|
-
queryset=Tenant.objects.all(),
|
|
526
|
-
required=False,
|
|
527
|
-
to_field_name="name",
|
|
528
|
-
help_text="Name of assigned tenant",
|
|
529
|
-
)
|
|
530
|
-
type = CSVChoiceField(choices=RackTypeChoices, required=False, help_text="Rack type")
|
|
531
|
-
width = forms.ChoiceField(choices=RackWidthChoices, help_text="Rail-to-rail width (in inches)")
|
|
532
|
-
outer_unit = CSVChoiceField(
|
|
533
|
-
choices=RackDimensionUnitChoices,
|
|
534
|
-
required=False,
|
|
535
|
-
help_text="Unit for outer dimensions",
|
|
536
|
-
)
|
|
537
|
-
|
|
538
|
-
class Meta:
|
|
539
|
-
model = Rack
|
|
540
|
-
fields = Rack.csv_headers
|
|
541
|
-
|
|
542
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
543
|
-
super().__init__(data, *args, **kwargs)
|
|
544
|
-
|
|
545
|
-
if data:
|
|
546
|
-
# Limit rack_group queryset by assigned location
|
|
547
|
-
params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
|
|
548
|
-
self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
|
|
549
|
-
|
|
550
|
-
|
|
551
440
|
class RackBulkEditForm(
|
|
552
441
|
TagsBulkEditFormMixin,
|
|
553
442
|
LocatableModelBulkEditFormMixin,
|
|
@@ -694,47 +583,6 @@ class RackReservationForm(NautobotModelForm, TenancyForm):
|
|
|
694
583
|
]
|
|
695
584
|
|
|
696
585
|
|
|
697
|
-
class RackReservationCSVForm(CustomFieldModelCSVForm):
|
|
698
|
-
location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name="name", help_text="Parent location")
|
|
699
|
-
rack_group = CSVModelChoiceField(
|
|
700
|
-
queryset=RackGroup.objects.all(),
|
|
701
|
-
to_field_name="name",
|
|
702
|
-
required=False,
|
|
703
|
-
help_text="Rack's group (if any)",
|
|
704
|
-
)
|
|
705
|
-
rack = CSVModelChoiceField(queryset=Rack.objects.all(), to_field_name="name", help_text="Rack")
|
|
706
|
-
units = SimpleArrayField(
|
|
707
|
-
base_field=forms.IntegerField(),
|
|
708
|
-
required=True,
|
|
709
|
-
help_text="Comma-separated list of individual unit numbers",
|
|
710
|
-
)
|
|
711
|
-
tenant = CSVModelChoiceField(
|
|
712
|
-
queryset=Tenant.objects.all(),
|
|
713
|
-
required=False,
|
|
714
|
-
to_field_name="name",
|
|
715
|
-
help_text="Assigned tenant",
|
|
716
|
-
)
|
|
717
|
-
|
|
718
|
-
class Meta:
|
|
719
|
-
model = RackReservation
|
|
720
|
-
fields = ("location", "rack_group", "rack", "units", "tenant", "description")
|
|
721
|
-
|
|
722
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
723
|
-
super().__init__(data, *args, **kwargs)
|
|
724
|
-
|
|
725
|
-
if data:
|
|
726
|
-
# Limit rack_group queryset by assigned location
|
|
727
|
-
params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
|
|
728
|
-
self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
|
|
729
|
-
|
|
730
|
-
# Limit rack queryset by assigned location and group
|
|
731
|
-
params = {
|
|
732
|
-
f"location__{self.fields['location'].to_field_name}": data.get("location"),
|
|
733
|
-
f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
|
|
734
|
-
}
|
|
735
|
-
self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
|
|
736
|
-
|
|
737
|
-
|
|
738
586
|
class RackReservationBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
739
587
|
pk = forms.ModelMultipleChoiceField(queryset=RackReservation.objects.all(), widget=forms.MultipleHiddenInput())
|
|
740
588
|
user = forms.ModelChoiceField(
|
|
@@ -791,12 +639,6 @@ class ManufacturerForm(NautobotModelForm):
|
|
|
791
639
|
]
|
|
792
640
|
|
|
793
641
|
|
|
794
|
-
class ManufacturerCSVForm(CustomFieldModelCSVForm):
|
|
795
|
-
class Meta:
|
|
796
|
-
model = Manufacturer
|
|
797
|
-
fields = Manufacturer.csv_headers
|
|
798
|
-
|
|
799
|
-
|
|
800
642
|
#
|
|
801
643
|
# Device types
|
|
802
644
|
#
|
|
@@ -835,6 +677,14 @@ class DeviceTypeForm(NautobotModelForm):
|
|
|
835
677
|
|
|
836
678
|
|
|
837
679
|
class DeviceTypeImportForm(BootstrapMixin, forms.ModelForm):
|
|
680
|
+
"""
|
|
681
|
+
Form for JSON/YAML import of DeviceType objects.
|
|
682
|
+
|
|
683
|
+
TODO: at some point we'll want to add general-purpose YAML serialization/deserialization,
|
|
684
|
+
similar to what we've done for CSV in 2.0, but for the moment we're leaving this as-is so that we can remain
|
|
685
|
+
at least nominally compatible with the netbox-community/devicetype-library repo.
|
|
686
|
+
"""
|
|
687
|
+
|
|
838
688
|
manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all(), to_field_name="name")
|
|
839
689
|
|
|
840
690
|
class Meta:
|
|
@@ -1403,6 +1253,14 @@ class DeviceBayTemplateBulkEditForm(NautobotBulkEditForm):
|
|
|
1403
1253
|
|
|
1404
1254
|
|
|
1405
1255
|
class ComponentTemplateImportForm(BootstrapMixin, CustomFieldModelCSVForm):
|
|
1256
|
+
"""
|
|
1257
|
+
Base form class for JSON/YAML import of device component templates as a part of the DeviceType import form/view.
|
|
1258
|
+
|
|
1259
|
+
TODO: at some point we'll want to switch to general-purpose YAML import support, similar to what we've done for
|
|
1260
|
+
CSV in 2.0, but for now we're keeping this as-is for nominal compatibility with the
|
|
1261
|
+
netbox-community/devicetype-library repository.
|
|
1262
|
+
"""
|
|
1263
|
+
|
|
1406
1264
|
def __init__(self, device_type, data=None, *args, **kwargs):
|
|
1407
1265
|
# Must pass the parent DeviceType on form initialization
|
|
1408
1266
|
data.update(
|
|
@@ -1573,19 +1431,6 @@ class PlatformForm(NautobotModelForm):
|
|
|
1573
1431
|
}
|
|
1574
1432
|
|
|
1575
1433
|
|
|
1576
|
-
class PlatformCSVForm(CustomFieldModelCSVForm):
|
|
1577
|
-
manufacturer = CSVModelChoiceField(
|
|
1578
|
-
queryset=Manufacturer.objects.all(),
|
|
1579
|
-
required=False,
|
|
1580
|
-
to_field_name="name",
|
|
1581
|
-
help_text="Limit platform assignments to this manufacturer",
|
|
1582
|
-
)
|
|
1583
|
-
|
|
1584
|
-
class Meta:
|
|
1585
|
-
model = Platform
|
|
1586
|
-
fields = Platform.csv_headers
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
1434
|
#
|
|
1590
1435
|
# Devices
|
|
1591
1436
|
#
|
|
@@ -1768,164 +1613,6 @@ class DeviceForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm, LocalC
|
|
|
1768
1613
|
return instance
|
|
1769
1614
|
|
|
1770
1615
|
|
|
1771
|
-
class BaseDeviceCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
1772
|
-
tenant = CSVModelChoiceField(
|
|
1773
|
-
queryset=Tenant.objects.all(),
|
|
1774
|
-
required=False,
|
|
1775
|
-
to_field_name="name",
|
|
1776
|
-
help_text="Assigned tenant",
|
|
1777
|
-
)
|
|
1778
|
-
manufacturer = CSVModelChoiceField(
|
|
1779
|
-
queryset=Manufacturer.objects.all(),
|
|
1780
|
-
to_field_name="name",
|
|
1781
|
-
help_text="Device type manufacturer",
|
|
1782
|
-
)
|
|
1783
|
-
device_type = CSVModelChoiceField(
|
|
1784
|
-
queryset=DeviceType.objects.all(),
|
|
1785
|
-
to_field_name="model",
|
|
1786
|
-
help_text="Device type model",
|
|
1787
|
-
)
|
|
1788
|
-
platform = CSVModelChoiceField(
|
|
1789
|
-
queryset=Platform.objects.all(),
|
|
1790
|
-
required=False,
|
|
1791
|
-
to_field_name="name",
|
|
1792
|
-
help_text="Assigned platform",
|
|
1793
|
-
)
|
|
1794
|
-
cluster = CSVModelChoiceField(
|
|
1795
|
-
queryset=Cluster.objects.all(),
|
|
1796
|
-
to_field_name="name",
|
|
1797
|
-
required=False,
|
|
1798
|
-
help_text="Virtualization cluster",
|
|
1799
|
-
)
|
|
1800
|
-
secrets_group = CSVModelChoiceField(
|
|
1801
|
-
queryset=SecretsGroup.objects.all(),
|
|
1802
|
-
required=False,
|
|
1803
|
-
to_field_name="name",
|
|
1804
|
-
help_text="Secrets group",
|
|
1805
|
-
)
|
|
1806
|
-
|
|
1807
|
-
class Meta:
|
|
1808
|
-
fields = []
|
|
1809
|
-
model = Device
|
|
1810
|
-
|
|
1811
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
1812
|
-
super().__init__(data, *args, **kwargs)
|
|
1813
|
-
|
|
1814
|
-
if data:
|
|
1815
|
-
# Limit device type queryset by manufacturer
|
|
1816
|
-
params = {f"manufacturer__{self.fields['manufacturer'].to_field_name}": data.get("manufacturer")}
|
|
1817
|
-
self.fields["device_type"].queryset = self.fields["device_type"].queryset.filter(**params)
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
class DeviceCSVForm(LocatableModelCSVFormMixin, BaseDeviceCSVForm, RoleRequiredRoleModelCSVFormMixin):
|
|
1821
|
-
rack_group = CSVModelChoiceField(
|
|
1822
|
-
queryset=RackGroup.objects.all(),
|
|
1823
|
-
to_field_name="name",
|
|
1824
|
-
required=False,
|
|
1825
|
-
help_text="Rack's group (if any)",
|
|
1826
|
-
)
|
|
1827
|
-
rack = CSVModelChoiceField(
|
|
1828
|
-
queryset=Rack.objects.all(),
|
|
1829
|
-
to_field_name="name",
|
|
1830
|
-
required=False,
|
|
1831
|
-
help_text="Assigned rack",
|
|
1832
|
-
)
|
|
1833
|
-
face = CSVChoiceField(choices=DeviceFaceChoices, required=False, help_text="Mounted rack face")
|
|
1834
|
-
device_redundancy_group = CSVModelChoiceField(
|
|
1835
|
-
queryset=DeviceRedundancyGroup.objects.all(),
|
|
1836
|
-
to_field_name="slug",
|
|
1837
|
-
required=False,
|
|
1838
|
-
help_text="Associated device redundancy group (slug)",
|
|
1839
|
-
)
|
|
1840
|
-
|
|
1841
|
-
class Meta(BaseDeviceCSVForm.Meta):
|
|
1842
|
-
fields = [
|
|
1843
|
-
"name",
|
|
1844
|
-
"role",
|
|
1845
|
-
"tenant",
|
|
1846
|
-
"manufacturer",
|
|
1847
|
-
"device_type",
|
|
1848
|
-
"platform",
|
|
1849
|
-
"serial",
|
|
1850
|
-
"asset_tag",
|
|
1851
|
-
"status",
|
|
1852
|
-
"location",
|
|
1853
|
-
"rack_group",
|
|
1854
|
-
"rack",
|
|
1855
|
-
"position",
|
|
1856
|
-
"face",
|
|
1857
|
-
"device_redundancy_group",
|
|
1858
|
-
"device_redundancy_group_priority",
|
|
1859
|
-
"cluster",
|
|
1860
|
-
"comments",
|
|
1861
|
-
]
|
|
1862
|
-
|
|
1863
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
1864
|
-
super().__init__(data, *args, **kwargs)
|
|
1865
|
-
|
|
1866
|
-
if data:
|
|
1867
|
-
# Limit rack_group queryset by assigned location
|
|
1868
|
-
params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
|
|
1869
|
-
self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
|
|
1870
|
-
|
|
1871
|
-
# Limit rack queryset by assigned location and group
|
|
1872
|
-
params = {
|
|
1873
|
-
f"location__{self.fields['location'].to_field_name}": data.get("location"),
|
|
1874
|
-
f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
|
|
1875
|
-
}
|
|
1876
|
-
self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
|
|
1877
|
-
|
|
1878
|
-
# 2.0 TODO: limit location queryset by assigned location
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
class ChildDeviceCSVForm(BaseDeviceCSVForm):
|
|
1882
|
-
parent = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Parent device")
|
|
1883
|
-
device_bay = CSVModelChoiceField(
|
|
1884
|
-
queryset=DeviceBay.objects.all(),
|
|
1885
|
-
to_field_name="name",
|
|
1886
|
-
help_text="Device bay in which this device is installed",
|
|
1887
|
-
)
|
|
1888
|
-
|
|
1889
|
-
class Meta(BaseDeviceCSVForm.Meta):
|
|
1890
|
-
fields = [
|
|
1891
|
-
"name",
|
|
1892
|
-
"role",
|
|
1893
|
-
"tenant",
|
|
1894
|
-
"manufacturer",
|
|
1895
|
-
"device_type",
|
|
1896
|
-
"platform",
|
|
1897
|
-
"serial",
|
|
1898
|
-
"asset_tag",
|
|
1899
|
-
"status",
|
|
1900
|
-
"parent",
|
|
1901
|
-
"device_bay",
|
|
1902
|
-
"cluster",
|
|
1903
|
-
"comments",
|
|
1904
|
-
]
|
|
1905
|
-
|
|
1906
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
1907
|
-
super().__init__(data, *args, **kwargs)
|
|
1908
|
-
|
|
1909
|
-
if data:
|
|
1910
|
-
# Limit device bay queryset by parent device
|
|
1911
|
-
params = {f"device__{self.fields['parent'].to_field_name}": data.get("parent")}
|
|
1912
|
-
self.fields["device_bay"].queryset = self.fields["device_bay"].queryset.filter(**params)
|
|
1913
|
-
|
|
1914
|
-
def clean(self):
|
|
1915
|
-
super().clean()
|
|
1916
|
-
|
|
1917
|
-
# Set parent_bay reverse relationship
|
|
1918
|
-
device_bay = self.cleaned_data.get("device_bay")
|
|
1919
|
-
if device_bay:
|
|
1920
|
-
self.instance.parent_bay = device_bay
|
|
1921
|
-
|
|
1922
|
-
# Inherit location and rack from parent device
|
|
1923
|
-
parent = self.cleaned_data.get("parent")
|
|
1924
|
-
if parent:
|
|
1925
|
-
self.instance.location = parent.location
|
|
1926
|
-
self.instance.rack = parent.rack
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
1616
|
class DeviceBulkEditForm(
|
|
1930
1617
|
TagsBulkEditFormMixin,
|
|
1931
1618
|
LocatableModelBulkEditFormMixin,
|
|
@@ -2173,15 +1860,6 @@ class ConsolePortBulkEditForm(
|
|
|
2173
1860
|
nullable_fields = ["label", "description"]
|
|
2174
1861
|
|
|
2175
1862
|
|
|
2176
|
-
class ConsolePortCSVForm(CustomFieldModelCSVForm):
|
|
2177
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
2178
|
-
type = CSVChoiceField(choices=ConsolePortTypeChoices, required=False, help_text="Port type")
|
|
2179
|
-
|
|
2180
|
-
class Meta:
|
|
2181
|
-
model = ConsolePort
|
|
2182
|
-
fields = ConsolePort.csv_headers
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
1863
|
#
|
|
2186
1864
|
# Console server ports
|
|
2187
1865
|
#
|
|
@@ -2240,15 +1918,6 @@ class ConsoleServerPortBulkEditForm(
|
|
|
2240
1918
|
nullable_fields = ["label", "description"]
|
|
2241
1919
|
|
|
2242
1920
|
|
|
2243
|
-
class ConsoleServerPortCSVForm(CustomFieldModelCSVForm):
|
|
2244
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
2245
|
-
type = CSVChoiceField(choices=ConsolePortTypeChoices, required=False, help_text="Port type")
|
|
2246
|
-
|
|
2247
|
-
class Meta:
|
|
2248
|
-
model = ConsoleServerPort
|
|
2249
|
-
fields = ConsoleServerPort.csv_headers
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
1921
|
#
|
|
2253
1922
|
# Power ports
|
|
2254
1923
|
#
|
|
@@ -2324,15 +1993,6 @@ class PowerPortBulkEditForm(
|
|
|
2324
1993
|
nullable_fields = ["label", "description"]
|
|
2325
1994
|
|
|
2326
1995
|
|
|
2327
|
-
class PowerPortCSVForm(CustomFieldModelCSVForm):
|
|
2328
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
2329
|
-
type = CSVChoiceField(choices=PowerPortTypeChoices, required=False, help_text="Port type")
|
|
2330
|
-
|
|
2331
|
-
class Meta:
|
|
2332
|
-
model = PowerPort
|
|
2333
|
-
fields = PowerPort.csv_headers
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
1996
|
#
|
|
2337
1997
|
# Power outlets
|
|
2338
1998
|
#
|
|
@@ -2437,46 +2097,6 @@ class PowerOutletBulkEditForm(
|
|
|
2437
2097
|
self.fields["power_port"].widget.attrs["disabled"] = True
|
|
2438
2098
|
|
|
2439
2099
|
|
|
2440
|
-
class PowerOutletCSVForm(CustomFieldModelCSVForm):
|
|
2441
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
2442
|
-
type = CSVChoiceField(choices=PowerOutletTypeChoices, required=False, help_text="Outlet type")
|
|
2443
|
-
power_port = CSVModelChoiceField(
|
|
2444
|
-
queryset=PowerPort.objects.all(),
|
|
2445
|
-
required=False,
|
|
2446
|
-
to_field_name="name",
|
|
2447
|
-
help_text="Local power port which feeds this outlet",
|
|
2448
|
-
)
|
|
2449
|
-
feed_leg = CSVChoiceField(
|
|
2450
|
-
choices=PowerOutletFeedLegChoices,
|
|
2451
|
-
required=False,
|
|
2452
|
-
help_text="Electrical phase (for three-phase circuits)",
|
|
2453
|
-
)
|
|
2454
|
-
|
|
2455
|
-
class Meta:
|
|
2456
|
-
model = PowerOutlet
|
|
2457
|
-
fields = PowerOutlet.csv_headers
|
|
2458
|
-
|
|
2459
|
-
def __init__(self, *args, **kwargs):
|
|
2460
|
-
super().__init__(*args, **kwargs)
|
|
2461
|
-
|
|
2462
|
-
# Limit PowerPort choices to those belonging to this device (or VC master)
|
|
2463
|
-
if self.is_bound:
|
|
2464
|
-
try:
|
|
2465
|
-
device = self.fields["device"].to_python(self.data["device"])
|
|
2466
|
-
except forms.ValidationError:
|
|
2467
|
-
device = None
|
|
2468
|
-
else:
|
|
2469
|
-
try:
|
|
2470
|
-
device = self.instance.device
|
|
2471
|
-
except Device.DoesNotExist:
|
|
2472
|
-
device = None
|
|
2473
|
-
|
|
2474
|
-
if device:
|
|
2475
|
-
self.fields["power_port"].queryset = PowerPort.objects.filter(device__in=[device, device.get_vc_master()])
|
|
2476
|
-
else:
|
|
2477
|
-
self.fields["power_port"].queryset = PowerPort.objects.none()
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
2100
|
#
|
|
2481
2101
|
# Interfaces
|
|
2482
2102
|
#
|
|
@@ -2520,7 +2140,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
|
|
|
2520
2140
|
queryset=VLAN.objects.all(),
|
|
2521
2141
|
required=False,
|
|
2522
2142
|
label="Untagged VLAN",
|
|
2523
|
-
brief_mode=False,
|
|
2524
2143
|
query_params={
|
|
2525
2144
|
"location": "null",
|
|
2526
2145
|
},
|
|
@@ -2529,7 +2148,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
|
|
|
2529
2148
|
queryset=VLAN.objects.all(),
|
|
2530
2149
|
required=False,
|
|
2531
2150
|
label="Tagged VLANs",
|
|
2532
|
-
brief_mode=False,
|
|
2533
2151
|
query_params={
|
|
2534
2152
|
"location": "null",
|
|
2535
2153
|
},
|
|
@@ -2538,10 +2156,6 @@ class InterfaceForm(InterfaceCommonForm, NautobotModelForm):
|
|
|
2538
2156
|
queryset=IPAddress.objects.all(),
|
|
2539
2157
|
required=False,
|
|
2540
2158
|
label="IP Addresses",
|
|
2541
|
-
brief_mode=False,
|
|
2542
|
-
query_params={
|
|
2543
|
-
"vrf": "$vrf",
|
|
2544
|
-
},
|
|
2545
2159
|
)
|
|
2546
2160
|
|
|
2547
2161
|
class Meta:
|
|
@@ -2665,7 +2279,6 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
|
|
2665
2279
|
untagged_vlan = DynamicModelChoiceField(
|
|
2666
2280
|
queryset=VLAN.objects.all(),
|
|
2667
2281
|
required=False,
|
|
2668
|
-
brief_mode=False,
|
|
2669
2282
|
query_params={
|
|
2670
2283
|
"available_on_device": "$device",
|
|
2671
2284
|
},
|
|
@@ -2673,7 +2286,6 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
|
|
2673
2286
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
|
2674
2287
|
queryset=VLAN.objects.all(),
|
|
2675
2288
|
required=False,
|
|
2676
|
-
brief_mode=False,
|
|
2677
2289
|
query_params={"available_on_device": "$device"},
|
|
2678
2290
|
)
|
|
2679
2291
|
field_order = (
|
|
@@ -2765,7 +2377,6 @@ class InterfaceBulkEditForm(
|
|
|
2765
2377
|
untagged_vlan = DynamicModelChoiceField(
|
|
2766
2378
|
queryset=VLAN.objects.all(),
|
|
2767
2379
|
required=False,
|
|
2768
|
-
brief_mode=False,
|
|
2769
2380
|
query_params={
|
|
2770
2381
|
"location": "null",
|
|
2771
2382
|
},
|
|
@@ -2773,7 +2384,6 @@ class InterfaceBulkEditForm(
|
|
|
2773
2384
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
|
2774
2385
|
queryset=VLAN.objects.all(),
|
|
2775
2386
|
required=False,
|
|
2776
|
-
brief_mode=False,
|
|
2777
2387
|
query_params={
|
|
2778
2388
|
"location": "null",
|
|
2779
2389
|
},
|
|
@@ -2846,67 +2456,6 @@ class InterfaceBulkEditForm(
|
|
|
2846
2456
|
self.cleaned_data["tagged_vlans"] = []
|
|
2847
2457
|
|
|
2848
2458
|
|
|
2849
|
-
class InterfaceCSVForm(CustomFieldModelCSVForm, StatusModelCSVFormMixin):
|
|
2850
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
2851
|
-
parent_interface = CSVModelChoiceField(
|
|
2852
|
-
queryset=Interface.objects.all(), required=False, to_field_name="name", help_text="Parent interface"
|
|
2853
|
-
)
|
|
2854
|
-
bridge = CSVModelChoiceField(
|
|
2855
|
-
queryset=Interface.objects.all(), required=False, to_field_name="name", help_text="Bridge interface"
|
|
2856
|
-
)
|
|
2857
|
-
lag = CSVModelChoiceField(
|
|
2858
|
-
queryset=Interface.objects.all(),
|
|
2859
|
-
required=False,
|
|
2860
|
-
to_field_name="name",
|
|
2861
|
-
help_text="Parent LAG interface",
|
|
2862
|
-
)
|
|
2863
|
-
type = CSVChoiceField(choices=InterfaceTypeChoices, help_text="Physical medium")
|
|
2864
|
-
mode = CSVChoiceField(
|
|
2865
|
-
choices=InterfaceModeChoices,
|
|
2866
|
-
required=False,
|
|
2867
|
-
help_text="IEEE 802.1Q operational mode (for L2 interfaces)",
|
|
2868
|
-
)
|
|
2869
|
-
|
|
2870
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
2871
|
-
super().__init__(data, *args, **kwargs)
|
|
2872
|
-
|
|
2873
|
-
if data:
|
|
2874
|
-
# Limit choices for parent, bridge, and LAG interfaces to the assigned device (or VC)
|
|
2875
|
-
device_name = data.get("device")
|
|
2876
|
-
if device_name is not None:
|
|
2877
|
-
device = Device.objects.filter(name=device_name).first()
|
|
2878
|
-
|
|
2879
|
-
filter_by = Q(device=device)
|
|
2880
|
-
|
|
2881
|
-
if device and device.virtual_chassis:
|
|
2882
|
-
filter_by |= Q(device__virtual_chassis=device.virtual_chassis)
|
|
2883
|
-
|
|
2884
|
-
self.fields["parent_interface"].queryset = (
|
|
2885
|
-
self.fields["parent_interface"]
|
|
2886
|
-
.queryset.filter(Q(filter_by))
|
|
2887
|
-
.exclude(type__in=NONCONNECTABLE_IFACE_TYPES)
|
|
2888
|
-
)
|
|
2889
|
-
self.fields["bridge"].queryset = self.fields["bridge"].queryset.filter(filter_by)
|
|
2890
|
-
|
|
2891
|
-
filter_by &= Q(type=InterfaceTypeChoices.TYPE_LAG)
|
|
2892
|
-
self.fields["lag"].queryset = self.fields["lag"].queryset.filter(filter_by)
|
|
2893
|
-
else:
|
|
2894
|
-
self.fields["parent_interface"].queryset = self.fields["parent_interface"].queryset.none()
|
|
2895
|
-
self.fields["bridge"].queryset = self.fields["bridge"].queryset.none()
|
|
2896
|
-
self.fields["lag"].queryset = self.fields["lag"].queryset.none()
|
|
2897
|
-
|
|
2898
|
-
class Meta:
|
|
2899
|
-
model = Interface
|
|
2900
|
-
fields = Interface.csv_headers
|
|
2901
|
-
|
|
2902
|
-
def clean_enabled(self):
|
|
2903
|
-
# Make sure enabled is True when it's not included in the uploaded data
|
|
2904
|
-
if "enabled" not in self.data:
|
|
2905
|
-
return True
|
|
2906
|
-
else:
|
|
2907
|
-
return self.cleaned_data["enabled"]
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
2459
|
#
|
|
2911
2460
|
# Front pass-through ports
|
|
2912
2461
|
#
|
|
@@ -3035,43 +2584,6 @@ class FrontPortBulkEditForm(
|
|
|
3035
2584
|
nullable_fields = ["label", "description"]
|
|
3036
2585
|
|
|
3037
2586
|
|
|
3038
|
-
class FrontPortCSVForm(CustomFieldModelCSVForm):
|
|
3039
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
3040
|
-
rear_port = CSVModelChoiceField(
|
|
3041
|
-
queryset=RearPort.objects.all(),
|
|
3042
|
-
to_field_name="name",
|
|
3043
|
-
help_text="Corresponding rear port",
|
|
3044
|
-
)
|
|
3045
|
-
type = CSVChoiceField(choices=PortTypeChoices, help_text="Physical medium classification")
|
|
3046
|
-
|
|
3047
|
-
class Meta:
|
|
3048
|
-
model = FrontPort
|
|
3049
|
-
fields = FrontPort.csv_headers
|
|
3050
|
-
help_texts = {
|
|
3051
|
-
"rear_port_position": "Mapped position on corresponding rear port",
|
|
3052
|
-
}
|
|
3053
|
-
|
|
3054
|
-
def __init__(self, *args, **kwargs):
|
|
3055
|
-
super().__init__(*args, **kwargs)
|
|
3056
|
-
|
|
3057
|
-
# Limit RearPort choices to those belonging to this device (or VC master)
|
|
3058
|
-
if self.is_bound:
|
|
3059
|
-
try:
|
|
3060
|
-
device = self.fields["device"].to_python(self.data["device"])
|
|
3061
|
-
except forms.ValidationError:
|
|
3062
|
-
device = None
|
|
3063
|
-
else:
|
|
3064
|
-
try:
|
|
3065
|
-
device = self.instance.device
|
|
3066
|
-
except Device.DoesNotExist:
|
|
3067
|
-
device = None
|
|
3068
|
-
|
|
3069
|
-
if device:
|
|
3070
|
-
self.fields["rear_port"].queryset = RearPort.objects.filter(device__in=[device, device.get_vc_master()])
|
|
3071
|
-
else:
|
|
3072
|
-
self.fields["rear_port"].queryset = RearPort.objects.none()
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
2587
|
#
|
|
3076
2588
|
# Rear pass-through ports
|
|
3077
2589
|
#
|
|
@@ -3145,19 +2657,6 @@ class RearPortBulkEditForm(
|
|
|
3145
2657
|
nullable_fields = ["label", "description"]
|
|
3146
2658
|
|
|
3147
2659
|
|
|
3148
|
-
class RearPortCSVForm(CustomFieldModelCSVForm):
|
|
3149
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
3150
|
-
type = CSVChoiceField(
|
|
3151
|
-
help_text="Physical medium classification",
|
|
3152
|
-
choices=PortTypeChoices,
|
|
3153
|
-
)
|
|
3154
|
-
|
|
3155
|
-
class Meta:
|
|
3156
|
-
model = RearPort
|
|
3157
|
-
fields = RearPort.csv_headers
|
|
3158
|
-
help_texts = {"positions": "Number of front ports which may be mapped"}
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
2660
|
#
|
|
3162
2661
|
# Device bays
|
|
3163
2662
|
#
|
|
@@ -3222,49 +2721,6 @@ class DeviceBayBulkEditForm(
|
|
|
3222
2721
|
nullable_fields = ["label", "description"]
|
|
3223
2722
|
|
|
3224
2723
|
|
|
3225
|
-
class DeviceBayCSVForm(CustomFieldModelCSVForm):
|
|
3226
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
3227
|
-
installed_device = CSVModelChoiceField(
|
|
3228
|
-
queryset=Device.objects.all(),
|
|
3229
|
-
required=False,
|
|
3230
|
-
to_field_name="name",
|
|
3231
|
-
help_text="Child device installed within this bay",
|
|
3232
|
-
error_messages={
|
|
3233
|
-
"invalid_choice": "Child device not found.",
|
|
3234
|
-
},
|
|
3235
|
-
)
|
|
3236
|
-
|
|
3237
|
-
class Meta:
|
|
3238
|
-
model = DeviceBay
|
|
3239
|
-
fields = DeviceBay.csv_headers
|
|
3240
|
-
|
|
3241
|
-
def __init__(self, *args, **kwargs):
|
|
3242
|
-
super().__init__(*args, **kwargs)
|
|
3243
|
-
|
|
3244
|
-
# Limit installed device choices to devices of the correct type and location
|
|
3245
|
-
if self.is_bound:
|
|
3246
|
-
try:
|
|
3247
|
-
device = self.fields["device"].to_python(self.data["device"])
|
|
3248
|
-
except forms.ValidationError:
|
|
3249
|
-
device = None
|
|
3250
|
-
else:
|
|
3251
|
-
try:
|
|
3252
|
-
device = self.instance.device
|
|
3253
|
-
except Device.DoesNotExist:
|
|
3254
|
-
device = None
|
|
3255
|
-
|
|
3256
|
-
if device:
|
|
3257
|
-
self.fields["installed_device"].queryset = Device.objects.filter(
|
|
3258
|
-
location=device.location,
|
|
3259
|
-
rack=device.rack,
|
|
3260
|
-
parent_bay__isnull=True,
|
|
3261
|
-
device_type__u_height=0,
|
|
3262
|
-
device_type__subdevice_role=SubdeviceRoleChoices.ROLE_CHILD,
|
|
3263
|
-
).exclude(pk=device.pk)
|
|
3264
|
-
else:
|
|
3265
|
-
self.fields["installed_device"].queryset = Interface.objects.none()
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
2724
|
#
|
|
3269
2725
|
# Inventory items
|
|
3270
2726
|
#
|
|
@@ -3325,15 +2781,6 @@ class InventoryItemCreateForm(ComponentCreateForm):
|
|
|
3325
2781
|
)
|
|
3326
2782
|
|
|
3327
2783
|
|
|
3328
|
-
class InventoryItemCSVForm(CustomFieldModelCSVForm):
|
|
3329
|
-
device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name")
|
|
3330
|
-
manufacturer = CSVModelChoiceField(queryset=Manufacturer.objects.all(), to_field_name="name", required=False)
|
|
3331
|
-
|
|
3332
|
-
class Meta:
|
|
3333
|
-
model = InventoryItem
|
|
3334
|
-
fields = InventoryItem.csv_headers
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
2784
|
class InventoryItemBulkCreateForm(
|
|
3338
2785
|
form_from_model(InventoryItem, ["manufacturer", "part_id", "serial", "asset_tag", "discovered", "tags"]),
|
|
3339
2786
|
DeviceBulkAddComponentForm,
|
|
@@ -3610,109 +3057,6 @@ class CableForm(NautobotModelForm):
|
|
|
3610
3057
|
error_messages = {"length": {"max_value": "Maximum length is 32767 (any unit)"}}
|
|
3611
3058
|
|
|
3612
3059
|
|
|
3613
|
-
class CableCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
3614
|
-
# Termination A
|
|
3615
|
-
side_a_device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Side A device")
|
|
3616
|
-
side_a_type = CSVContentTypeField(
|
|
3617
|
-
queryset=ContentType.objects.all(),
|
|
3618
|
-
limit_choices_to=CABLE_TERMINATION_MODELS,
|
|
3619
|
-
help_text="Side A type",
|
|
3620
|
-
)
|
|
3621
|
-
side_a_name = forms.CharField(help_text="Side A component name")
|
|
3622
|
-
|
|
3623
|
-
# Termination B
|
|
3624
|
-
side_b_device = CSVModelChoiceField(queryset=Device.objects.all(), to_field_name="name", help_text="Side B device")
|
|
3625
|
-
side_b_type = CSVContentTypeField(
|
|
3626
|
-
queryset=ContentType.objects.all(),
|
|
3627
|
-
limit_choices_to=CABLE_TERMINATION_MODELS,
|
|
3628
|
-
help_text="Side B type",
|
|
3629
|
-
)
|
|
3630
|
-
side_b_name = forms.CharField(help_text="Side B component name")
|
|
3631
|
-
|
|
3632
|
-
# Cable attributes
|
|
3633
|
-
type = CSVChoiceField(
|
|
3634
|
-
choices=CableTypeChoices,
|
|
3635
|
-
required=False,
|
|
3636
|
-
help_text="Physical medium classification",
|
|
3637
|
-
)
|
|
3638
|
-
length_unit = CSVChoiceField(choices=CableLengthUnitChoices, required=False, help_text="Length unit")
|
|
3639
|
-
|
|
3640
|
-
class Meta:
|
|
3641
|
-
model = Cable
|
|
3642
|
-
fields = [
|
|
3643
|
-
"side_a_device",
|
|
3644
|
-
"side_a_type",
|
|
3645
|
-
"side_a_name",
|
|
3646
|
-
"side_b_device",
|
|
3647
|
-
"side_b_type",
|
|
3648
|
-
"side_b_name",
|
|
3649
|
-
"type",
|
|
3650
|
-
"status",
|
|
3651
|
-
"label",
|
|
3652
|
-
"color",
|
|
3653
|
-
"length",
|
|
3654
|
-
"length_unit",
|
|
3655
|
-
]
|
|
3656
|
-
help_texts = {
|
|
3657
|
-
"color": mark_safe("RGB color in hexadecimal (e.g. <code>00ff00</code>)"),
|
|
3658
|
-
"status": "Connection status",
|
|
3659
|
-
}
|
|
3660
|
-
|
|
3661
|
-
def _clean_side(self, side):
|
|
3662
|
-
"""
|
|
3663
|
-
Derive a Cable's A/B termination objects.
|
|
3664
|
-
|
|
3665
|
-
:param side: 'a' or 'b'
|
|
3666
|
-
"""
|
|
3667
|
-
assert side in "ab", f"Invalid side designation: {side}"
|
|
3668
|
-
|
|
3669
|
-
device = self.cleaned_data.get(f"side_{side}_device")
|
|
3670
|
-
content_type = self.cleaned_data.get(f"side_{side}_type")
|
|
3671
|
-
name = self.cleaned_data.get(f"side_{side}_name")
|
|
3672
|
-
if not device or not content_type or not name:
|
|
3673
|
-
return None
|
|
3674
|
-
|
|
3675
|
-
model = content_type.model_class()
|
|
3676
|
-
try:
|
|
3677
|
-
termination_object = model.objects.get(device=device, name=name)
|
|
3678
|
-
if termination_object.cable is not None:
|
|
3679
|
-
raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected")
|
|
3680
|
-
except ObjectDoesNotExist:
|
|
3681
|
-
raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}")
|
|
3682
|
-
|
|
3683
|
-
setattr(self.instance, f"termination_{side}", termination_object)
|
|
3684
|
-
return termination_object
|
|
3685
|
-
|
|
3686
|
-
def clean_side_a_name(self):
|
|
3687
|
-
return self._clean_side("a")
|
|
3688
|
-
|
|
3689
|
-
def clean_side_b_name(self):
|
|
3690
|
-
return self._clean_side("b")
|
|
3691
|
-
|
|
3692
|
-
def clean_length_unit(self):
|
|
3693
|
-
# Avoid trying to save as NULL
|
|
3694
|
-
length_unit = self.cleaned_data.get("length_unit", None)
|
|
3695
|
-
return length_unit if length_unit is not None else ""
|
|
3696
|
-
|
|
3697
|
-
def add_error(self, field, error):
|
|
3698
|
-
# Edge Case: some fields in error are not properties in this instance
|
|
3699
|
-
# e.g: termination_a_id not an property in CableCSVForm, This would raise a ValueError Exception
|
|
3700
|
-
# Solution: convert those fields to its equivalent in CableCSVForm
|
|
3701
|
-
# e.g: termination_a_id > side_a_name
|
|
3702
|
-
|
|
3703
|
-
final_error = error
|
|
3704
|
-
if hasattr(error, "error_dict"):
|
|
3705
|
-
error_dict = error.error_dict
|
|
3706
|
-
termination_keys = [key for key in error_dict.keys() if key.startswith("termination")]
|
|
3707
|
-
for error_field in termination_keys:
|
|
3708
|
-
side_value = error_field.split("_")[1]
|
|
3709
|
-
error_msg = error_dict.pop(error_field)
|
|
3710
|
-
error_dict[f"side_{side_value}_name"] = error_msg
|
|
3711
|
-
|
|
3712
|
-
final_error = ValidationError(error_dict)
|
|
3713
|
-
super().add_error(field, final_error)
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
3060
|
class CableBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
|
|
3717
3061
|
pk = forms.ModelMultipleChoiceField(queryset=Cable.objects.all(), widget=forms.MultipleHiddenInput)
|
|
3718
3062
|
type = forms.ChoiceField(
|
|
@@ -3792,6 +3136,7 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
|
|
|
3792
3136
|
queryset=Device.objects.all(),
|
|
3793
3137
|
required=False,
|
|
3794
3138
|
label="Device",
|
|
3139
|
+
to_field_name="name",
|
|
3795
3140
|
query_params={"location": "$location"},
|
|
3796
3141
|
)
|
|
3797
3142
|
|
|
@@ -3802,6 +3147,7 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
|
|
|
3802
3147
|
queryset=Device.objects.all(),
|
|
3803
3148
|
required=False,
|
|
3804
3149
|
label="Device",
|
|
3150
|
+
to_field_name="name",
|
|
3805
3151
|
query_params={"location": "$location"},
|
|
3806
3152
|
)
|
|
3807
3153
|
|
|
@@ -3812,6 +3158,7 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
|
|
|
3812
3158
|
queryset=Device.objects.all(),
|
|
3813
3159
|
required=False,
|
|
3814
3160
|
label="Device",
|
|
3161
|
+
to_field_name="name",
|
|
3815
3162
|
query_params={"location": "$location"},
|
|
3816
3163
|
)
|
|
3817
3164
|
|
|
@@ -3979,19 +3326,6 @@ class VirtualChassisBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
|
|
|
3979
3326
|
nullable_fields = ["domain"]
|
|
3980
3327
|
|
|
3981
3328
|
|
|
3982
|
-
class VirtualChassisCSVForm(CustomFieldModelCSVForm):
|
|
3983
|
-
master = CSVModelChoiceField(
|
|
3984
|
-
queryset=Device.objects.all(),
|
|
3985
|
-
to_field_name="name",
|
|
3986
|
-
required=False,
|
|
3987
|
-
help_text="Master device",
|
|
3988
|
-
)
|
|
3989
|
-
|
|
3990
|
-
class Meta:
|
|
3991
|
-
model = VirtualChassis
|
|
3992
|
-
fields = VirtualChassis.csv_headers
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
3329
|
class VirtualChassisFilterForm(NautobotFilterForm):
|
|
3996
3330
|
model = VirtualChassis
|
|
3997
3331
|
q = forms.CharField(required=False, label="Search")
|
|
@@ -4034,22 +3368,6 @@ class PowerPanelForm(LocatableModelFormMixin, NautobotModelForm):
|
|
|
4034
3368
|
]
|
|
4035
3369
|
|
|
4036
3370
|
|
|
4037
|
-
class PowerPanelCSVForm(LocatableModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
4038
|
-
rack_group = CSVModelChoiceField(queryset=RackGroup.objects.all(), required=False, to_field_name="name")
|
|
4039
|
-
|
|
4040
|
-
class Meta:
|
|
4041
|
-
model = PowerPanel
|
|
4042
|
-
fields = PowerPanel.csv_headers
|
|
4043
|
-
|
|
4044
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
4045
|
-
super().__init__(data, *args, **kwargs)
|
|
4046
|
-
|
|
4047
|
-
if data:
|
|
4048
|
-
# Limit rack_group queryset by assigned location
|
|
4049
|
-
params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
|
|
4050
|
-
self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
3371
|
class PowerPanelBulkEditForm(
|
|
4054
3372
|
TagsBulkEditFormMixin,
|
|
4055
3373
|
LocatableModelBulkEditFormMixin,
|
|
@@ -4123,53 +3441,6 @@ class PowerFeedForm(NautobotModelForm):
|
|
|
4123
3441
|
}
|
|
4124
3442
|
|
|
4125
3443
|
|
|
4126
|
-
class PowerFeedCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
4127
|
-
location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name="name", help_text="Assigned location")
|
|
4128
|
-
power_panel = CSVModelChoiceField(
|
|
4129
|
-
queryset=PowerPanel.objects.all(),
|
|
4130
|
-
to_field_name="name",
|
|
4131
|
-
help_text="Upstream power panel",
|
|
4132
|
-
)
|
|
4133
|
-
rack_group = CSVModelChoiceField(
|
|
4134
|
-
queryset=RackGroup.objects.all(),
|
|
4135
|
-
to_field_name="name",
|
|
4136
|
-
required=False,
|
|
4137
|
-
help_text="Rack's group (if any)",
|
|
4138
|
-
)
|
|
4139
|
-
rack = CSVModelChoiceField(
|
|
4140
|
-
queryset=Rack.objects.all(),
|
|
4141
|
-
to_field_name="name",
|
|
4142
|
-
required=False,
|
|
4143
|
-
help_text="Rack",
|
|
4144
|
-
)
|
|
4145
|
-
type = CSVChoiceField(choices=PowerFeedTypeChoices, required=False, help_text="Primary or redundant")
|
|
4146
|
-
supply = CSVChoiceField(choices=PowerFeedSupplyChoices, required=False, help_text="Supply type (AC/DC)")
|
|
4147
|
-
phase = CSVChoiceField(choices=PowerFeedPhaseChoices, required=False, help_text="Single or three-phase")
|
|
4148
|
-
|
|
4149
|
-
class Meta:
|
|
4150
|
-
model = PowerFeed
|
|
4151
|
-
fields = PowerFeed.csv_headers
|
|
4152
|
-
|
|
4153
|
-
def __init__(self, data=None, *args, **kwargs):
|
|
4154
|
-
super().__init__(data, *args, **kwargs)
|
|
4155
|
-
|
|
4156
|
-
if data:
|
|
4157
|
-
# Limit power_panel queryset by location
|
|
4158
|
-
params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
|
|
4159
|
-
self.fields["power_panel"].queryset = self.fields["power_panel"].queryset.filter(**params)
|
|
4160
|
-
|
|
4161
|
-
# Limit rack_group queryset by location
|
|
4162
|
-
params = {f"location__{self.fields['location'].to_field_name}": data.get("location")}
|
|
4163
|
-
self.fields["rack_group"].queryset = self.fields["rack_group"].queryset.filter(**params)
|
|
4164
|
-
|
|
4165
|
-
# Limit rack queryset by location and group
|
|
4166
|
-
params = {
|
|
4167
|
-
f"location__{self.fields['location'].to_field_name}": data.get("location"),
|
|
4168
|
-
f"rack_group__{self.fields['rack_group'].to_field_name}": data.get("rack_group"),
|
|
4169
|
-
}
|
|
4170
|
-
self.fields["rack"].queryset = self.fields["rack"].queryset.filter(**params)
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
3444
|
class PowerFeedBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
|
|
4174
3445
|
pk = forms.ModelMultipleChoiceField(queryset=PowerFeed.objects.all(), widget=forms.MultipleHiddenInput)
|
|
4175
3446
|
power_panel = DynamicModelChoiceField(queryset=PowerPanel.objects.all(), required=False)
|
|
@@ -4286,20 +3557,3 @@ class DeviceRedundancyGroupBulkEditForm(
|
|
|
4286
3557
|
"failover_strategy",
|
|
4287
3558
|
"secrets_group",
|
|
4288
3559
|
]
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
class DeviceRedundancyGroupCSVForm(StatusModelCSVFormMixin, CustomFieldModelCSVForm):
|
|
4292
|
-
failover_strategy = CSVChoiceField(
|
|
4293
|
-
choices=DeviceRedundancyGroupFailoverStrategyChoices, required=False, help_text="Failover Strategy"
|
|
4294
|
-
)
|
|
4295
|
-
|
|
4296
|
-
secrets_group = CSVModelChoiceField(
|
|
4297
|
-
queryset=SecretsGroup.objects.all(),
|
|
4298
|
-
required=False,
|
|
4299
|
-
to_field_name="name",
|
|
4300
|
-
help_text="Secrets group",
|
|
4301
|
-
)
|
|
4302
|
-
|
|
4303
|
-
class Meta:
|
|
4304
|
-
model = DeviceRedundancyGroup
|
|
4305
|
-
fields = DeviceRedundancyGroup.csv_headers
|