nautobot 2.0.0a2__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/__init__.py +1 -5
- nautobot/apps/api.py +6 -8
- nautobot/apps/forms.py +0 -2
- nautobot/apps/ui.py +0 -8
- nautobot/circuits/api/serializers.py +9 -119
- nautobot/circuits/api/urls.py +1 -1
- nautobot/circuits/api/views.py +0 -1
- nautobot/circuits/choices.py +0 -2
- nautobot/circuits/filters.py +7 -6
- nautobot/circuits/forms.py +3 -73
- nautobot/circuits/migrations/0001_initial_part_1.py +0 -1
- nautobot/circuits/migrations/0002_initial_part_2.py +0 -1
- nautobot/circuits/migrations/0003_auto_slug.py +0 -1
- nautobot/circuits/migrations/0004_increase_provider_account_length.py +0 -1
- nautobot/circuits/migrations/0005_providernetwork.py +0 -1
- nautobot/circuits/migrations/0006_cache_circuit_terminations.py +0 -1
- nautobot/circuits/migrations/0007_circuitterminations_primary_model.py +0 -1
- nautobot/circuits/migrations/0008_add_natural_indexing.py +0 -1
- nautobot/circuits/migrations/0009_circuittermination_location.py +0 -1
- nautobot/circuits/migrations/0010_rename_foreign_keys_and_related_names.py +0 -1
- nautobot/circuits/migrations/0011_remove_site_foreign_key_from_circuit_termination_class.py +0 -1
- nautobot/circuits/migrations/0012_created_datetime.py +0 -1
- nautobot/circuits/migrations/0013_alter_circuittermination__path.py +0 -1
- nautobot/circuits/migrations/0014_related_name_changes.py +1 -2
- nautobot/circuits/migrations/0015_remove_circuittype_provider_slug.py +20 -0
- 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 -93
- nautobot/circuits/navigation.py +14 -69
- nautobot/circuits/signals.py +0 -2
- nautobot/circuits/tables.py +42 -5
- nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -1
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -1
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +1 -1
- nautobot/circuits/templates/circuits/provider_create.html +0 -1
- nautobot/circuits/templates/circuits/provider_retrieve.html +1 -1
- nautobot/circuits/tests/integration/test_relationships.py +13 -16
- nautobot/circuits/tests/test_api.py +13 -43
- nautobot/circuits/tests/test_filters.py +20 -15
- nautobot/circuits/tests/test_models.py +7 -3
- nautobot/circuits/tests/test_views.py +57 -67
- nautobot/circuits/views.py +18 -9
- nautobot/core/api/__init__.py +8 -2
- nautobot/core/api/authentication.py +0 -3
- 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 +3 -3
- nautobot/core/api/parsers.py +154 -0
- nautobot/core/api/renderers.py +153 -2
- nautobot/core/api/schema.py +47 -3
- nautobot/core/api/serializers.py +377 -37
- 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 -75
- nautobot/core/apps/__init__.py +138 -221
- nautobot/core/celery/__init__.py +112 -41
- nautobot/core/celery/backends.py +19 -13
- 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 +21 -113
- nautobot/core/{cli.py → cli/__init__.py} +1 -2
- nautobot/core/cli/__main__.py +3 -0
- nautobot/core/constants.py +25 -43
- 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 +39 -68
- nautobot/core/forms/forms.py +27 -27
- nautobot/core/forms/utils.py +7 -59
- nautobot/core/forms/widgets.py +0 -1
- nautobot/core/graphql/__init__.py +2 -2
- 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/celery.py +0 -1
- nautobot/core/management/commands/generate_test_data.py +18 -13
- nautobot/core/management/commands/post_upgrade.py +24 -24
- nautobot/core/management/commands/validate_models.py +0 -1
- nautobot/core/middleware.py +0 -1
- nautobot/core/models/__init__.py +26 -1
- nautobot/core/models/fields.py +24 -5
- nautobot/core/models/generics.py +2 -46
- nautobot/core/models/managers.py +5 -0
- nautobot/core/models/name_color_content_types.py +1 -19
- nautobot/core/models/tree_queries.py +14 -4
- nautobot/core/models/utils.py +9 -10
- nautobot/core/models/validators.py +17 -8
- nautobot/core/releases.py +8 -10
- nautobot/core/settings.py +81 -53
- 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 +4 -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 -25
- 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 -29
- nautobot/core/templatetags/helpers.py +1 -1
- nautobot/core/testing/__init__.py +47 -44
- nautobot/core/testing/api.py +365 -47
- nautobot/core/testing/filters.py +12 -7
- nautobot/core/testing/integration.py +1 -1
- nautobot/core/testing/migrations.py +2 -0
- nautobot/core/testing/mixins.py +22 -12
- nautobot/core/testing/schema.py +2 -1
- nautobot/core/testing/views.py +28 -51
- 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/runner.py +0 -1
- nautobot/core/tests/test_api.py +290 -24
- nautobot/core/tests/test_authentication.py +57 -14
- nautobot/core/tests/test_checks.py +0 -7
- nautobot/core/tests/test_choices.py +0 -1
- nautobot/core/tests/test_filters.py +117 -110
- nautobot/core/tests/test_forms.py +47 -110
- nautobot/core/tests/test_graphql.py +158 -135
- nautobot/core/tests/test_logging.py +4 -1
- nautobot/core/tests/test_managers.py +3 -5
- nautobot/core/tests/test_models.py +2 -0
- nautobot/core/tests/test_ordering.py +0 -2
- nautobot/core/tests/test_paginator.py +3 -1
- nautobot/core/tests/test_releases.py +12 -12
- nautobot/core/tests/test_templatetags_helpers.py +7 -4
- nautobot/core/tests/test_utils.py +112 -78
- nautobot/core/tests/test_views.py +12 -17
- nautobot/core/tests/test_views_utils.py +6 -9
- nautobot/core/utils/data.py +17 -0
- nautobot/core/utils/deprecation.py +13 -20
- nautobot/core/utils/filtering.py +53 -9
- nautobot/core/utils/git.py +12 -4
- nautobot/core/utils/lookup.py +3 -1
- nautobot/core/utils/requests.py +23 -116
- nautobot/core/views/__init__.py +1 -2
- nautobot/core/views/generic.py +131 -119
- nautobot/core/views/mixins.py +53 -62
- nautobot/core/views/paginator.py +0 -1
- nautobot/core/views/renderers.py +14 -12
- nautobot/core/views/utils.py +87 -4
- nautobot/dcim/api/serializers.py +160 -672
- nautobot/dcim/api/urls.py +1 -1
- nautobot/dcim/api/views.py +7 -46
- nautobot/dcim/choices.py +2 -25
- nautobot/dcim/elevations.py +0 -1
- nautobot/dcim/factory.py +15 -4
- nautobot/dcim/filters/__init__.py +42 -13
- nautobot/dcim/form_mixins.py +1 -27
- nautobot/dcim/forms.py +58 -797
- nautobot/dcim/management/commands/trace_paths.py +0 -1
- nautobot/dcim/migrations/0001_initial_part_1.py +0 -1
- nautobot/dcim/migrations/0002_initial_part_2.py +0 -1
- nautobot/dcim/migrations/0003_initial_part_3.py +0 -1
- nautobot/dcim/migrations/0004_initial_part_4.py +0 -1
- nautobot/dcim/migrations/0005_device_local_context_schema.py +0 -1
- nautobot/dcim/migrations/0006_auto_slug.py +0 -1
- nautobot/dcim/migrations/0007_device_secrets_group.py +0 -1
- nautobot/dcim/migrations/0008_increase_all_serial_lengths.py +0 -1
- nautobot/dcim/migrations/0009_add_natural_indexing.py +0 -1
- nautobot/dcim/migrations/0010_interface_status.py +0 -1
- nautobot/dcim/migrations/0011_interface_status_data_migration.py +0 -1
- nautobot/dcim/migrations/0012_interface_parent_bridge.py +0 -1
- nautobot/dcim/migrations/0013_location_location_type.py +0 -1
- nautobot/dcim/migrations/0014_location_status_data_migration.py +0 -1
- nautobot/dcim/migrations/0015_device_components__changeloggedmodel.py +0 -1
- nautobot/dcim/migrations/0016_device_components__timestamp_data_migration.py +0 -1
- nautobot/dcim/migrations/0017_locationtype_nestable.py +0 -1
- nautobot/dcim/migrations/0018_device_redundancy_group.py +0 -1
- nautobot/dcim/migrations/0019_device_redundancy_group_data_migration.py +0 -1
- nautobot/dcim/migrations/0020_move_site_fields_to_location_model.py +0 -1
- nautobot/dcim/migrations/0021_mptt_to_tree_queries.py +0 -1
- nautobot/dcim/migrations/0022_interface_mac_address_data_migration.py +0 -1
- nautobot/dcim/migrations/0023_alter_interface_mac_address.py +0 -1
- nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -2
- nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -14
- nautobot/dcim/migrations/0026_rename_device_and_rack_role.py +0 -1
- nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -2
- nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -2
- nautobot/dcim/migrations/0029_add_tree_managers_and_foreign_keys_pre_data_migration.py +0 -1
- nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
- nautobot/dcim/migrations/0031_rename_path_end_point_related_name.py +0 -1
- nautobot/dcim/migrations/0032_remove_site_foreign_key_from_dcim_models.py +0 -1
- nautobot/dcim/migrations/0033_created_datetime.py +0 -1
- nautobot/dcim/migrations/0034_fixup_fks_and_related_names.py +0 -1
- nautobot/dcim/migrations/0035_related_name_changes.py +1 -2
- nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -2
- nautobot/dcim/migrations/0037_interface_ip_addresses_m2m.py +0 -1
- nautobot/dcim/migrations/0038_alter_location_managers.py +0 -1
- nautobot/dcim/migrations/0039_remove_slug.py +24 -0
- nautobot/dcim/migrations/0040_tagsfield.py +109 -0
- nautobot/dcim/migrations/0041_ipam__namespaces.py +25 -0
- nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
- nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
- nautobot/dcim/models/cables.py +4 -35
- nautobot/dcim/models/device_component_templates.py +7 -2
- nautobot/dcim/models/device_components.py +26 -203
- nautobot/dcim/models/devices.py +30 -152
- nautobot/dcim/models/locations.py +3 -64
- nautobot/dcim/models/power.py +3 -51
- nautobot/dcim/models/racks.py +7 -86
- nautobot/dcim/navigation.py +141 -467
- nautobot/dcim/signals.py +0 -2
- nautobot/dcim/tables/devices.py +8 -5
- nautobot/dcim/tables/devicetypes.py +1 -1
- nautobot/dcim/tables/locations.py +2 -2
- nautobot/dcim/tables/power.py +2 -2
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/device.html +15 -4
- nautobot/dcim/templates/dcim/device_edit.html +6 -0
- nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +0 -1
- nautobot/dcim/templates/dcim/devicetype.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +4 -0
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/interface_edit.html +1 -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/integration/test_cable_connect_form.py +4 -4
- nautobot/dcim/tests/test_api.py +202 -130
- nautobot/dcim/tests/test_cablepaths.py +47 -42
- nautobot/dcim/tests/test_filters.py +156 -134
- nautobot/dcim/tests/test_forms.py +12 -213
- nautobot/dcim/tests/test_graphql.py +8 -3
- nautobot/dcim/tests/test_migrations.py +6 -11
- nautobot/dcim/tests/test_models.py +208 -158
- nautobot/dcim/tests/test_natural_ordering.py +12 -14
- nautobot/dcim/tests/test_signals.py +7 -4
- nautobot/dcim/tests/test_views.py +270 -264
- nautobot/dcim/urls.py +21 -26
- nautobot/dcim/views.py +14 -156
- 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 +179 -197
- nautobot/docs/administration/nautobot-server.md +9 -24
- nautobot/docs/administration/nautobot-shell.md +6 -6
- nautobot/docs/administration/replicating-nautobot.md +0 -10
- nautobot/docs/configuration/index.md +9 -9
- nautobot/docs/configuration/optional-settings.md +32 -61
- nautobot/docs/configuration/required-settings.md +11 -52
- nautobot/docs/development/application-registry.md +2 -13
- nautobot/docs/development/best-practices.md +2 -1
- nautobot/docs/development/docker-compose-advanced-use-cases.md +1 -1
- nautobot/docs/development/extending-models.md +15 -17
- nautobot/docs/development/generic-views.md +0 -2
- nautobot/docs/development/getting-started.md +56 -6
- nautobot/docs/development/navigation-menu.md +22 -93
- nautobot/docs/development/react-ui.md +105 -0
- nautobot/docs/development/release-checklist.md +3 -3
- nautobot/docs/development/role-internals.md +1 -3
- nautobot/docs/development/style-guide.md +6 -4
- nautobot/docs/development/templates.md +2 -1
- nautobot/docs/docker/index.md +16 -14
- nautobot/docs/index.md +7 -3
- nautobot/docs/installation/index.md +4 -1
- nautobot/docs/installation/migrating-from-netbox.md +12 -43
- nautobot/docs/installation/migrating-from-postgresql.md +1 -1
- 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 +190 -636
- nautobot/docs/installation/upgrading.md +5 -2
- 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/dynamicgroup.md +9 -9
- 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/ipam/vrf.md +0 -3
- nautobot/docs/models/virtualization/virtualmachine.md +3 -0
- nautobot/docs/plugins/development.md +92 -24
- nautobot/docs/release-notes/version-1.5.md +96 -0
- nautobot/docs/release-notes/version-2.0.md +216 -0
- nautobot/docs/requirements.txt +5 -4
- 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 -159
- nautobot/extras/api/serializers.py +165 -706
- nautobot/extras/api/urls.py +1 -1
- nautobot/extras/api/views.py +295 -282
- nautobot/extras/apps.py +4 -7
- nautobot/extras/choices.py +11 -22
- 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/factory.py +1 -3
- nautobot/extras/filters/__init__.py +49 -47
- nautobot/extras/filters/mixins.py +10 -8
- nautobot/extras/forms/forms.py +72 -148
- nautobot/extras/forms/mixins.py +34 -57
- nautobot/extras/health_checks.py +0 -33
- nautobot/extras/jobs.py +387 -566
- nautobot/extras/management/__init__.py +55 -48
- nautobot/extras/management/commands/renaturalize.py +0 -1
- nautobot/extras/management/commands/runjob.py +24 -62
- nautobot/extras/management/commands/webhook_receiver.py +0 -1
- nautobot/extras/managers.py +30 -7
- nautobot/extras/migrations/0001_initial_part_1.py +0 -1
- nautobot/extras/migrations/0002_initial_part_2.py +0 -1
- nautobot/extras/migrations/0003_initial_part_3.py +0 -1
- nautobot/extras/migrations/0004_populate_default_status_records.py +0 -1
- nautobot/extras/migrations/0005_configcontext_device_types.py +0 -1
- nautobot/extras/migrations/0006_graphqlquery.py +0 -1
- nautobot/extras/migrations/0007_configcontextschema.py +0 -1
- nautobot/extras/migrations/0008_jobresult__custom_field_data.py +0 -1
- nautobot/extras/migrations/0009_computedfield.py +0 -1
- nautobot/extras/migrations/0010_change_cf_validation_max_min_field_to_bigint.py +0 -1
- nautobot/extras/migrations/0011_fileattachment_fileproxy.py +0 -1
- nautobot/extras/migrations/0012_healthchecktestmodel.py +0 -1
- nautobot/extras/migrations/0013_default_fallback_value_computedfield.py +0 -1
- nautobot/extras/migrations/0014_auto_slug.py +0 -1
- nautobot/extras/migrations/0015_scheduled_job.py +0 -1
- nautobot/extras/migrations/0016_secret.py +0 -1
- nautobot/extras/migrations/0017_joblogentry.py +0 -1
- nautobot/extras/migrations/0018_joblog_data_migration.py +0 -2
- nautobot/extras/migrations/0019_joblogentry__meta_options__related_name.py +0 -1
- nautobot/extras/migrations/0020_customfield_changelog.py +0 -1
- nautobot/extras/migrations/0021_customfield_changelog_data.py +0 -1
- nautobot/extras/migrations/0022_objectchange_object_datav2.py +0 -1
- nautobot/extras/migrations/0023_job_model.py +0 -1
- nautobot/extras/migrations/0024_job_data_migration.py +0 -1
- nautobot/extras/migrations/0025_add_advanced_ui_boolean_to_customfield_conputedfield_and_relationship.py +0 -1
- nautobot/extras/migrations/0026_job_add_gitrepository_fk.py +0 -1
- nautobot/extras/migrations/0027_job_gitrepository_data_migration.py +0 -1
- nautobot/extras/migrations/0028_job_reduce_source.py +0 -1
- nautobot/extras/migrations/0029_dynamicgroup.py +0 -1
- nautobot/extras/migrations/0030_webhook_alter_unique_together.py +0 -1
- nautobot/extras/migrations/0031_tag_content_types.py +0 -1
- nautobot/extras/migrations/0032_tag_content_types_data_migration.py +0 -1
- nautobot/extras/migrations/0033_add__optimized_indexing.py +0 -1
- nautobot/extras/migrations/0034_alter_fileattachment_mimetype.py +0 -1
- nautobot/extras/migrations/0035_scheduledjob_crontab.py +0 -1
- nautobot/extras/migrations/0036_job_add_has_sensitive_variables.py +0 -1
- nautobot/extras/migrations/0037_configcontextschema__remove_name_unique__create_constraint_unique_name_owner.py +0 -1
- nautobot/extras/migrations/0038_configcontext_locations.py +0 -1
- nautobot/extras/migrations/0039_objectchange__add_change_context.py +0 -1
- nautobot/extras/migrations/0040_dynamicgroup__dynamicgroupmembership.py +0 -1
- nautobot/extras/migrations/0041_jobresult_job_kwargs.py +0 -1
- nautobot/extras/migrations/0042_job__add_is_job_hook_receiver.py +0 -1
- nautobot/extras/migrations/0043_note.py +0 -1
- nautobot/extras/migrations/0044_add_job_hook.py +0 -1
- nautobot/extras/migrations/0045_add_custom_field_slug.py +0 -1
- nautobot/extras/migrations/0046_populate_custom_field_slug_label.py +0 -1
- nautobot/extras/migrations/0047_enforce_custom_field_slug.py +0 -1
- nautobot/extras/migrations/0048_alter_objectchange_change_context_detail.py +0 -1
- nautobot/extras/migrations/0049_alter_tag_slug.py +0 -1
- nautobot/extras/migrations/0050_customfield_grouping.py +0 -1
- nautobot/extras/migrations/0051_add_job_task_queues.py +0 -1
- nautobot/extras/migrations/0052_configcontext_device_redundancy_groups.py +0 -1
- nautobot/extras/migrations/0053_relationship_required_on.py +0 -1
- nautobot/extras/migrations/0054_scheduledjob_kwargs_request_user_change.py +0 -1
- nautobot/extras/migrations/0055_configcontext_dynamic_groups.py +0 -1
- nautobot/extras/migrations/0056_objectchange_add_reverse_time_idx.py +0 -1
- nautobot/extras/migrations/0057_jobbutton.py +0 -1
- 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 -2
- nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -2
- nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -8
- nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -33
- nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -2
- nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -2
- nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
- nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -2
- nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -3
- nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -2
- nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -2
- 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 -2
- 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 -10
- nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -2
- 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/0078_remove_slug.py +45 -0
- 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 -12
- nautobot/extras/models/jobs.py +190 -323
- nautobot/extras/models/mixins.py +0 -71
- nautobot/extras/models/models.py +1 -22
- nautobot/extras/models/relationships.py +20 -21
- nautobot/extras/models/roles.py +0 -23
- nautobot/extras/models/secrets.py +2 -31
- 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 -121
- nautobot/extras/plugins/utils.py +0 -3
- nautobot/extras/plugins/validators.py +5 -4
- nautobot/extras/plugins/views.py +16 -4
- nautobot/extras/querysets.py +1 -7
- nautobot/extras/registry.py +3 -0
- nautobot/extras/signals.py +26 -60
- nautobot/extras/tables.py +42 -49
- 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/dynamicgroup_edit.html +0 -1
- 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 +14 -17
- nautobot/extras/templates/extras/jobresult.html +24 -6
- nautobot/extras/templates/extras/objectchange_list.html +1 -1
- nautobot/extras/templates/extras/scheduledjob.html +2 -2
- nautobot/extras/templates/extras/secret.html +28 -0
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -1
- nautobot/extras/templatetags/custom_links.py +0 -2
- nautobot/extras/templatetags/job_buttons.py +1 -0
- nautobot/extras/templatetags/plugins.py +0 -1
- 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/__init__.py +3 -3
- nautobot/extras/tests/integration/test_computedfields.py +1 -1
- nautobot/extras/tests/integration/test_configcontextschema.py +7 -5
- nautobot/extras/tests/integration/test_customfields.py +4 -2
- nautobot/extras/tests/integration/test_dynamicgroups.py +2 -2
- nautobot/extras/tests/integration/test_jobs.py +25 -27
- nautobot/extras/tests/integration/test_notes.py +8 -4
- nautobot/extras/tests/integration/test_plugins.py +4 -4
- nautobot/extras/tests/integration/test_relationships.py +2 -2
- nautobot/extras/tests/test_api.py +371 -381
- nautobot/extras/tests/test_changelog.py +17 -16
- nautobot/extras/tests/test_context_managers.py +5 -6
- nautobot/extras/tests/test_customfields.py +112 -73
- nautobot/extras/tests/test_datasources.py +191 -117
- nautobot/extras/tests/test_dynamicgroups.py +45 -68
- nautobot/extras/tests/test_filters.py +170 -130
- nautobot/extras/tests/test_forms.py +107 -109
- nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
- nautobot/extras/tests/test_jobs.py +271 -273
- nautobot/extras/tests/test_management.py +3 -6
- nautobot/extras/tests/test_migrations.py +5 -3
- nautobot/extras/tests/test_models.py +121 -173
- nautobot/extras/tests/test_notes.py +0 -1
- nautobot/extras/tests/test_plugins.py +55 -89
- nautobot/extras/tests/test_relationships.py +174 -130
- nautobot/extras/tests/test_tags.py +6 -12
- nautobot/extras/tests/test_utils.py +31 -1
- nautobot/extras/tests/test_views.py +223 -184
- nautobot/extras/tests/test_webhooks.py +16 -15
- nautobot/extras/urls.py +69 -69
- nautobot/extras/utils.py +137 -163
- nautobot/extras/views.py +81 -153
- nautobot/ipam/api/fields.py +17 -0
- nautobot/ipam/api/serializers.py +77 -164
- nautobot/ipam/api/urls.py +4 -1
- nautobot/ipam/api/views.py +28 -19
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/choices.py +5 -12
- nautobot/ipam/constants.py +1 -0
- nautobot/ipam/factory.py +41 -30
- nautobot/ipam/filters.py +58 -25
- nautobot/ipam/forms.py +82 -211
- nautobot/ipam/graphql/types.py +0 -9
- nautobot/ipam/lookups.py +13 -8
- nautobot/ipam/management/commands/__init__.py +0 -0
- nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
- nautobot/ipam/migrations/0001_initial_part_1.py +0 -1
- nautobot/ipam/migrations/0002_initial_part_2.py +0 -1
- nautobot/ipam/migrations/0003_remove_max_length.py +0 -1
- nautobot/ipam/migrations/0004_fixup_p2p_broadcast.py +0 -1
- nautobot/ipam/migrations/0005_auto_slug.py +0 -1
- nautobot/ipam/migrations/0006_ipaddress_nat_outside_list.py +0 -1
- nautobot/ipam/migrations/0007_add_natural_indexing.py +0 -1
- nautobot/ipam/migrations/0008_prefix_vlan_vlangroup_location.py +0 -1
- nautobot/ipam/migrations/0009_alter_vlan_name.py +0 -1
- nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -2
- nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -39
- nautobot/ipam/migrations/0012_rename_ipam_roles.py +0 -1
- nautobot/ipam/migrations/0013_delete_role.py +0 -1
- nautobot/ipam/migrations/0014_rename_foreign_keys_and_related_names.py +0 -1
- nautobot/ipam/migrations/0015_prefix_add_type.py +0 -1
- nautobot/ipam/migrations/0016_prefix_type_data_migration.py +0 -3
- nautobot/ipam/migrations/0017_prefix_remove_is_pool.py +0 -1
- nautobot/ipam/migrations/0018_remove_site_foreign_key_from_ipam_models.py +0 -1
- nautobot/ipam/migrations/0019_created_datetime.py +0 -1
- nautobot/ipam/migrations/0020_related_name_changes.py +1 -2
- nautobot/ipam/migrations/0021_prefix_add_rir_and_date_allocated.py +0 -1
- nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +3 -5
- nautobot/ipam/migrations/0023_delete_aggregate.py +0 -1
- nautobot/ipam/migrations/0024_interface_to_ipaddress_m2m.py +0 -1
- nautobot/ipam/migrations/0025_interface_ipaddress_m2m_data_migration.py +0 -1
- nautobot/ipam/migrations/0026_ipaddress_remove_assigned_object.py +0 -1
- nautobot/ipam/migrations/0027_remove_rir_slug.py +16 -0
- nautobot/ipam/migrations/0028_tagsfield.py +44 -0
- nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
- nautobot/ipam/migrations/0030_ipam__namespaces.py +231 -0
- 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 +579 -368
- nautobot/ipam/navigation.py +36 -159
- nautobot/ipam/querysets.py +117 -90
- nautobot/ipam/signals.py +89 -0
- nautobot/ipam/tables.py +86 -28
- nautobot/ipam/templates/ipam/ipaddress.html +14 -30
- nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -0
- 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 +42 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
- nautobot/ipam/templates/ipam/prefix.html +27 -33
- nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
- nautobot/ipam/templates/ipam/vlangroup.html +0 -13
- nautobot/ipam/templates/ipam/vrf.html +6 -4
- nautobot/ipam/templates/ipam/vrf_edit.html +20 -2
- nautobot/ipam/tests/integration/test_prefixes.py +4 -27
- nautobot/ipam/tests/test_api.py +60 -61
- nautobot/ipam/tests/test_filters.py +187 -126
- nautobot/ipam/tests/test_forms.py +12 -6
- nautobot/ipam/tests/test_graphql.py +8 -6
- nautobot/ipam/tests/test_migrations.py +8 -13
- nautobot/ipam/tests/test_models.py +426 -274
- nautobot/ipam/tests/test_ordering.py +6 -3
- nautobot/ipam/tests/test_querysets.py +340 -96
- nautobot/ipam/tests/test_views.py +100 -55
- nautobot/ipam/urls.py +28 -5
- nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
- nautobot/ipam/utils/migrations.py +713 -0
- nautobot/ipam/views.py +237 -122
- nautobot/project-static/docs/404.html +1399 -166
- nautobot/project-static/docs/additional-features/caching.html +1416 -320
- nautobot/project-static/docs/additional-features/change-logging.html +1389 -190
- nautobot/project-static/docs/additional-features/config-contexts.html +1389 -190
- nautobot/project-static/docs/additional-features/graphql.html +1389 -190
- nautobot/project-static/docs/additional-features/healthcheck.html +1389 -190
- nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1393 -190
- nautobot/project-static/docs/additional-features/jobs.html +1677 -460
- nautobot/project-static/docs/additional-features/napalm.html +1389 -190
- nautobot/project-static/docs/additional-features/prometheus-metrics.html +1389 -190
- nautobot/project-static/docs/additional-features/template-filters.html +1389 -190
- nautobot/project-static/docs/administration/celery-queues.html +1389 -190
- nautobot/project-static/docs/administration/nautobot-server.html +1553 -375
- nautobot/project-static/docs/administration/nautobot-shell.html +1395 -196
- nautobot/project-static/docs/administration/permissions.html +1389 -190
- nautobot/project-static/docs/administration/replicating-nautobot.html +1387 -207
- nautobot/project-static/docs/apps/index.html +1389 -190
- nautobot/project-static/docs/apps/nautobot-apps.html +1387 -175
- nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js +29 -0
- nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js.map +8 -0
- nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js → search.208ed371.min.js} +9 -15
- nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js.map → search.208ed371.min.js.map} +4 -4
- nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css.map +1 -0
- nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1775 -590
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1389 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3588 -1922
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1461 -262
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1401 -170
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1396 -191
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2095 -894
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2357 -1194
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2258 -940
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1389 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1400 -201
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11068 -7861
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2867 -2224
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1389 -190
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2641 -1573
- nautobot/project-static/docs/configuration/authentication/ldap.html +1389 -190
- nautobot/project-static/docs/configuration/authentication/remote.html +1389 -190
- nautobot/project-static/docs/configuration/authentication/sso.html +1389 -190
- nautobot/project-static/docs/configuration/index.html +1398 -199
- nautobot/project-static/docs/configuration/optional-settings.html +1418 -274
- nautobot/project-static/docs/configuration/required-settings.html +1419 -287
- nautobot/project-static/docs/core-functionality/circuits.html +1446 -247
- nautobot/project-static/docs/core-functionality/device-types.html +1448 -249
- nautobot/project-static/docs/core-functionality/devices.html +1452 -249
- nautobot/project-static/docs/core-functionality/ipam.html +1452 -253
- nautobot/project-static/docs/core-functionality/power.html +1448 -249
- nautobot/project-static/docs/core-functionality/secrets.html +1448 -249
- nautobot/project-static/docs/core-functionality/services.html +1448 -249
- nautobot/project-static/docs/core-functionality/sites-and-racks.html +1448 -249
- nautobot/project-static/docs/core-functionality/tenancy.html +1448 -249
- nautobot/project-static/docs/core-functionality/virtualization.html +1452 -249
- nautobot/project-static/docs/core-functionality/vlans.html +1448 -249
- nautobot/project-static/docs/development/application-registry.html +1393 -214
- nautobot/project-static/docs/development/best-practices.html +1392 -192
- nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1390 -191
- nautobot/project-static/docs/development/extending-models.html +1443 -257
- nautobot/project-static/docs/development/generic-views.html +1403 -174
- nautobot/project-static/docs/development/getting-started.html +1568 -262
- nautobot/project-static/docs/development/homepage.html +1389 -190
- nautobot/project-static/docs/development/index.html +1389 -190
- nautobot/project-static/docs/development/model-features.html +1389 -190
- nautobot/project-static/docs/development/natural-keys.html +1389 -190
- nautobot/project-static/docs/development/navigation-menu.html +1451 -330
- nautobot/project-static/docs/development/react-ui.html +4199 -0
- nautobot/project-static/docs/development/release-checklist.html +1392 -193
- nautobot/project-static/docs/development/role-internals.html +1402 -172
- nautobot/project-static/docs/development/style-guide.html +1399 -199
- nautobot/project-static/docs/development/templates.html +1391 -191
- nautobot/project-static/docs/development/testing.html +1389 -190
- nautobot/project-static/docs/development/user-preferences.html +1389 -190
- nautobot/project-static/docs/docker/index.html +1408 -206
- nautobot/project-static/docs/index.html +1397 -180
- nautobot/project-static/docs/installation/centos.html +1401 -170
- nautobot/project-static/docs/installation/external-authentication.html +1389 -190
- nautobot/project-static/docs/installation/http-server.html +1389 -190
- nautobot/project-static/docs/installation/index.html +1394 -191
- nautobot/project-static/docs/installation/migrating-from-netbox.html +1452 -305
- nautobot/project-static/docs/installation/migrating-from-postgresql.html +1390 -191
- nautobot/project-static/docs/installation/nautobot.html +1390 -191
- nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1389 -190
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +1401 -170
- nautobot/project-static/docs/installation/services.html +1389 -190
- 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 +1401 -170
- nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +4254 -1923
- nautobot/project-static/docs/installation/upgrading.html +1395 -192
- nautobot/project-static/docs/models/circuits/circuit.html +1427 -174
- nautobot/project-static/docs/models/circuits/circuittermination.html +1427 -174
- nautobot/project-static/docs/models/circuits/circuittype.html +1427 -174
- nautobot/project-static/docs/models/circuits/provider.html +1427 -174
- nautobot/project-static/docs/models/circuits/providernetwork.html +1427 -174
- nautobot/project-static/docs/models/dcim/cable.html +1458 -174
- nautobot/project-static/docs/models/dcim/consoleport.html +1427 -174
- nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/consoleserverport.html +1427 -174
- nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/device.html +1431 -174
- nautobot/project-static/docs/models/dcim/devicebay.html +1427 -174
- nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1522 -177
- nautobot/project-static/docs/models/dcim/devicetype.html +1427 -174
- nautobot/project-static/docs/models/dcim/frontport.html +1427 -174
- nautobot/project-static/docs/models/dcim/frontporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/interface.html +1427 -174
- nautobot/project-static/docs/models/dcim/interfacetemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/inventoryitem.html +1427 -174
- nautobot/project-static/docs/models/dcim/location.html +1427 -174
- nautobot/project-static/docs/models/dcim/locationtype.html +1427 -174
- nautobot/project-static/docs/models/dcim/manufacturer.html +1427 -174
- nautobot/project-static/docs/models/dcim/platform.html +1427 -174
- nautobot/project-static/docs/models/dcim/powerfeed.html +1425 -172
- nautobot/project-static/docs/models/dcim/poweroutlet.html +1427 -174
- nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/powerpanel.html +1425 -172
- nautobot/project-static/docs/models/dcim/powerport.html +1427 -174
- nautobot/project-static/docs/models/dcim/powerporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/rack.html +1427 -174
- nautobot/project-static/docs/models/dcim/rackgroup.html +1427 -174
- nautobot/project-static/docs/models/dcim/rackreservation.html +1427 -174
- nautobot/project-static/docs/models/dcim/rearport.html +1427 -174
- nautobot/project-static/docs/models/dcim/rearporttemplate.html +1427 -174
- nautobot/project-static/docs/models/dcim/region.html +1401 -170
- nautobot/project-static/docs/models/dcim/site.html +1401 -170
- nautobot/project-static/docs/models/dcim/virtualchassis.html +1425 -172
- nautobot/project-static/docs/models/extras/computedfield.html +1393 -194
- nautobot/project-static/docs/models/extras/configcontext.html +1465 -174
- nautobot/project-static/docs/models/extras/configcontextschema.html +1421 -168
- nautobot/project-static/docs/models/extras/customfield.html +1389 -190
- nautobot/project-static/docs/models/extras/customlink.html +1389 -190
- nautobot/project-static/docs/models/extras/dynamicgroup.html +1398 -199
- nautobot/project-static/docs/models/extras/exporttemplate.html +1389 -190
- nautobot/project-static/docs/models/extras/gitrepository.html +1393 -190
- nautobot/project-static/docs/models/extras/graphqlquery.html +1469 -171
- nautobot/project-static/docs/models/extras/imageattachment.html +1434 -181
- nautobot/project-static/docs/models/extras/job.html +1411 -157
- nautobot/project-static/docs/models/extras/jobbutton.html +1410 -207
- nautobot/project-static/docs/models/extras/jobhook.html +1397 -194
- nautobot/project-static/docs/models/extras/joblogentry.html +1408 -155
- nautobot/project-static/docs/models/extras/jobresult.html +1417 -159
- nautobot/project-static/docs/models/extras/note.html +1389 -190
- nautobot/project-static/docs/models/extras/relationship.html +1391 -192
- nautobot/project-static/docs/models/extras/role.html +1495 -198
- nautobot/project-static/docs/models/extras/secret.html +1492 -201
- nautobot/project-static/docs/models/extras/secretsgroup.html +1410 -157
- nautobot/project-static/docs/models/extras/status.html +1381 -221
- nautobot/project-static/docs/models/extras/tag.html +1389 -190
- nautobot/project-static/docs/models/extras/webhook.html +1389 -190
- nautobot/project-static/docs/models/ipam/ipaddress.html +1488 -200
- nautobot/project-static/docs/models/ipam/prefix.html +1410 -157
- nautobot/project-static/docs/models/ipam/rir.html +1410 -157
- nautobot/project-static/docs/models/ipam/routetarget.html +1410 -157
- nautobot/project-static/docs/models/ipam/service.html +1410 -157
- nautobot/project-static/docs/models/ipam/vlan.html +1410 -157
- nautobot/project-static/docs/models/ipam/vlangroup.html +1410 -157
- nautobot/project-static/docs/models/ipam/vrf.html +1410 -161
- nautobot/project-static/docs/models/tenancy/tenant.html +1412 -159
- nautobot/project-static/docs/models/tenancy/tenantgroup.html +1412 -159
- nautobot/project-static/docs/models/users/objectpermission.html +1462 -171
- nautobot/project-static/docs/models/users/token.html +1410 -157
- nautobot/project-static/docs/models/virtualization/cluster.html +1410 -157
- nautobot/project-static/docs/models/virtualization/clustergroup.html +1410 -157
- nautobot/project-static/docs/models/virtualization/clustertype.html +1410 -157
- nautobot/project-static/docs/models/virtualization/virtualmachine.html +1414 -157
- nautobot/project-static/docs/models/virtualization/vminterface.html +1410 -157
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/plugins/development.html +1916 -646
- nautobot/project-static/docs/plugins/index.html +1389 -190
- nautobot/project-static/docs/plugins/porting-from-netbox.html +1389 -190
- nautobot/project-static/docs/release-notes/index.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.0.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.1.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.2.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.3.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.4.html +1389 -190
- nautobot/project-static/docs/release-notes/version-1.5.html +2016 -397
- nautobot/project-static/docs/release-notes/version-2.0.html +1935 -287
- nautobot/project-static/docs/requirements.txt +5 -4
- nautobot/project-static/docs/rest-api/authentication.html +1389 -190
- nautobot/project-static/docs/rest-api/filtering.html +1389 -190
- nautobot/project-static/docs/rest-api/overview.html +2002 -576
- 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 +1390 -191
- nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1392 -193
- nautobot/project-static/docs/user-guides/getting-started/index.html +1388 -189
- nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1388 -189
- nautobot/project-static/docs/user-guides/getting-started/ipam.html +1386 -187
- nautobot/project-static/docs/user-guides/getting-started/platforms.html +1448 -249
- nautobot/project-static/docs/user-guides/getting-started/regions.html +1411 -212
- nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1395 -196
- nautobot/project-static/docs/user-guides/getting-started/tenants.html +1448 -249
- nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1448 -249
- nautobot/project-static/docs/user-guides/git-data-source.html +1405 -206
- nautobot/project-static/docs/user-guides/graphql.html +1402 -203
- nautobot/project-static/docs/user-guides/relationships.html +1448 -249
- nautobot/project-static/docs/user-guides/s3-django-storage.html +1448 -249
- nautobot/project-static/js/forms.js +16 -9
- nautobot/project-static/js/theme.js +5 -0
- nautobot/tenancy/api/serializers.py +4 -34
- nautobot/tenancy/api/urls.py +1 -1
- nautobot/tenancy/filters/__init__.py +9 -7
- nautobot/tenancy/filters/mixins.py +3 -2
- nautobot/tenancy/forms.py +3 -36
- nautobot/tenancy/migrations/0001_initial.py +0 -1
- nautobot/tenancy/migrations/0002_auto_slug.py +0 -1
- nautobot/tenancy/migrations/0003_mptt_to_tree_queries.py +0 -1
- nautobot/tenancy/migrations/0004_change_tree_manager_on_tree_models.py +0 -1
- nautobot/tenancy/migrations/0005_rename_foreign_keys_and_related_names.py +0 -1
- nautobot/tenancy/migrations/0006_created_datetime.py +0 -1
- nautobot/tenancy/migrations/0007_remove_tenant_tenantgroup_slug.py +20 -0
- nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
- nautobot/tenancy/models.py +0 -30
- nautobot/tenancy/navigation.py +6 -39
- nautobot/tenancy/tables.py +4 -4
- nautobot/tenancy/templates/tenancy/tenant.html +12 -12
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -1
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- nautobot/tenancy/tests/test_api.py +1 -12
- nautobot/tenancy/tests/test_filters.py +20 -12
- nautobot/tenancy/tests/test_views.py +11 -29
- nautobot/tenancy/urls.py +10 -10
- nautobot/tenancy/views.py +0 -3
- 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/migrations/0001_initial.py +0 -1
- nautobot/users/migrations/0002_token_ordering_by_created.py +0 -1
- nautobot/users/migrations/0003_alter_user_options.py +0 -1
- nautobot/users/migrations/0004_alter_user_managers.py +0 -1
- nautobot/users/tests/test_api.py +109 -28
- nautobot/users/tests/test_filters.py +0 -4
- nautobot/users/tests/test_models.py +0 -1
- nautobot/users/views.py +0 -7
- nautobot/virtualization/api/serializers.py +18 -132
- nautobot/virtualization/api/urls.py +1 -1
- nautobot/virtualization/api/views.py +1 -22
- nautobot/virtualization/choices.py +0 -2
- nautobot/virtualization/filters.py +12 -7
- nautobot/virtualization/forms.py +21 -117
- nautobot/virtualization/migrations/0001_initial.py +0 -1
- nautobot/virtualization/migrations/0002_virtualmachine_local_context_schema.py +0 -1
- nautobot/virtualization/migrations/0003_vminterface_verbose_name.py +0 -1
- nautobot/virtualization/migrations/0004_auto_slug.py +0 -1
- nautobot/virtualization/migrations/0005_add_natural_indexing.py +0 -1
- nautobot/virtualization/migrations/0006_vminterface_status.py +0 -1
- nautobot/virtualization/migrations/0007_vminterface_status_data_migration.py +0 -1
- nautobot/virtualization/migrations/0008_vminterface_parent.py +0 -1
- nautobot/virtualization/migrations/0009_cluster_location.py +0 -1
- nautobot/virtualization/migrations/0010_vminterface_mac_address_data_migration.py +0 -1
- nautobot/virtualization/migrations/0011_alter_vminterface_mac_address.py +0 -1
- nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -2
- nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -12
- nautobot/virtualization/migrations/0014_rename_virtualmachine_roles.py +0 -1
- nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -2
- nautobot/virtualization/migrations/0016_remove_site_foreign_key_from_cluster_class.py +0 -1
- nautobot/virtualization/migrations/0017_created_datetime.py +0 -1
- nautobot/virtualization/migrations/0018_related_name_changes.py +1 -2
- nautobot/virtualization/migrations/0019_vminterface_ip_addresses_m2m.py +0 -1
- nautobot/virtualization/migrations/0020_remove_clustergroup_clustertype_slug.py +20 -0
- 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/0023_ipam__namespaces.py +25 -0
- nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
- nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
- nautobot/virtualization/models.py +39 -131
- nautobot/virtualization/navigation.py +18 -99
- nautobot/virtualization/tables.py +4 -4
- nautobot/virtualization/templates/virtualization/virtualmachine.html +13 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
- nautobot/virtualization/tests/test_api.py +42 -52
- nautobot/virtualization/tests/test_filters.py +98 -75
- nautobot/virtualization/tests/test_models.py +36 -13
- nautobot/virtualization/tests/test_views.py +68 -73
- nautobot/virtualization/urls.py +10 -10
- nautobot/virtualization/views.py +8 -14
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +15 -22
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +987 -834
- {nautobot-2.0.0a2.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 -42
- nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -47
- 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 -19
- 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 -143
- nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js +0 -29
- nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js.map +0 -8
- nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js +0 -18
- nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js.map +0 -8
- nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css.map +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css.map +0 -1
- nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css.map +0 -1
- 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.0a2.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
nautobot/extras/api/views.py
CHANGED
|
@@ -3,7 +3,7 @@ from django.conf import settings
|
|
|
3
3
|
from django.contrib.contenttypes.models import ContentType
|
|
4
4
|
from django.forms import ValidationError as FormsValidationError
|
|
5
5
|
from django.http import Http404
|
|
6
|
-
from django.shortcuts import get_object_or_404
|
|
6
|
+
from django.shortcuts import get_object_or_404, render
|
|
7
7
|
from django.utils import timezone
|
|
8
8
|
from drf_spectacular.types import OpenApiTypes
|
|
9
9
|
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
|
@@ -12,6 +12,7 @@ from graphql import GraphQLError
|
|
|
12
12
|
from rest_framework import status
|
|
13
13
|
from rest_framework.decorators import action
|
|
14
14
|
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied, ValidationError
|
|
15
|
+
from rest_framework.filters import OrderingFilter
|
|
15
16
|
from rest_framework.parsers import JSONParser, MultiPartParser
|
|
16
17
|
from rest_framework.permissions import IsAuthenticated
|
|
17
18
|
from rest_framework.response import Response
|
|
@@ -20,7 +21,7 @@ from rest_framework import mixins, viewsets
|
|
|
20
21
|
|
|
21
22
|
from nautobot.core.api.authentication import TokenPermissions
|
|
22
23
|
from nautobot.core.api.filter_backends import NautobotFilterBackend
|
|
23
|
-
from nautobot.core.api.utils import get_serializer_for_model
|
|
24
|
+
from nautobot.core.api.utils import get_data_for_serializer_parameter, get_serializer_for_model
|
|
24
25
|
from nautobot.core.api.views import (
|
|
25
26
|
BulkDestroyModelMixin,
|
|
26
27
|
BulkUpdateModelMixin,
|
|
@@ -30,10 +31,9 @@ from nautobot.core.api.views import (
|
|
|
30
31
|
from nautobot.core.exceptions import CeleryWorkerNotRunningException
|
|
31
32
|
from nautobot.core.graphql import execute_saved_query
|
|
32
33
|
from nautobot.core.models.querysets import count_related
|
|
33
|
-
from nautobot.core.utils.
|
|
34
|
+
from nautobot.core.utils.lookup import get_table_for_model
|
|
34
35
|
from nautobot.extras import filters
|
|
35
|
-
from nautobot.extras.choices import JobExecutionType
|
|
36
|
-
from nautobot.extras.datasources import enqueue_pull_git_repository_and_refresh_data
|
|
36
|
+
from nautobot.extras.choices import JobExecutionType
|
|
37
37
|
from nautobot.extras.filters import RoleFilterSet
|
|
38
38
|
from nautobot.extras.models import (
|
|
39
39
|
ComputedField,
|
|
@@ -66,9 +66,9 @@ from nautobot.extras.models import (
|
|
|
66
66
|
Webhook,
|
|
67
67
|
)
|
|
68
68
|
from nautobot.extras.models import CustomField, CustomFieldChoice
|
|
69
|
-
from nautobot.extras.
|
|
70
|
-
from nautobot.extras.utils import
|
|
71
|
-
from . import
|
|
69
|
+
from nautobot.extras.secrets.exceptions import SecretError
|
|
70
|
+
from nautobot.extras.utils import get_worker_count
|
|
71
|
+
from . import serializers
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
class ExtrasRootView(APIRootView):
|
|
@@ -112,6 +112,84 @@ class NotesViewSetMixin:
|
|
|
112
112
|
return self.get_paginated_response(serializer.data)
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
# TODO: This is part of the drf-react-template work towards auto-generating create/edit form UI from the REST API.
|
|
116
|
+
# TODO: Why is this in extras instead of core?
|
|
117
|
+
class FormFieldsViewSetMixin:
|
|
118
|
+
"""TODO: docstring needed."""
|
|
119
|
+
|
|
120
|
+
# TODO: shouldn't this function generally be named "get_field_groups" not "get_field_group"?
|
|
121
|
+
def get_field_group(self):
|
|
122
|
+
return []
|
|
123
|
+
|
|
124
|
+
# TODO: schema doesn't *look* correct to me based on a reading of the below code. Should this even be in the schema?
|
|
125
|
+
@extend_schema(
|
|
126
|
+
responses={
|
|
127
|
+
200: {
|
|
128
|
+
"type": "array",
|
|
129
|
+
"items": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"properties": {
|
|
132
|
+
"type": {"type": "string"},
|
|
133
|
+
"label": {"type": "string"},
|
|
134
|
+
"required": {"type": "string"},
|
|
135
|
+
"help_text": {"type": "string"},
|
|
136
|
+
"choices": {"type": "array", "items": {"type": "array"}},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
@action(detail=False, url_path="form-fields", methods=["get"])
|
|
143
|
+
def form_fields(self, request):
|
|
144
|
+
groups = self.get_field_group()
|
|
145
|
+
model = self.queryset.model
|
|
146
|
+
fields = get_data_for_serializer_parameter(model)
|
|
147
|
+
if groups:
|
|
148
|
+
data = {
|
|
149
|
+
group_name: [fields.get(field) for field in group_fields] for group_name, group_fields in groups.items()
|
|
150
|
+
}
|
|
151
|
+
else:
|
|
152
|
+
model_name = model._meta.model_name
|
|
153
|
+
data = {model_name.capitalize(): fields.values()}
|
|
154
|
+
|
|
155
|
+
return Response(data)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# TODO: This is part of the drf-react-template work towards auto-generating create/edit form UI from the REST API.
|
|
159
|
+
# TODO: Why is this in extras instead of core?
|
|
160
|
+
class TableFieldsViewSetMixin:
|
|
161
|
+
"""TODO: docstring needed."""
|
|
162
|
+
|
|
163
|
+
# TODO: this schema is definitely incorrect. Should this view even be in the schema?
|
|
164
|
+
@extend_schema(
|
|
165
|
+
responses={
|
|
166
|
+
200: {
|
|
167
|
+
"type": "array",
|
|
168
|
+
"items": {
|
|
169
|
+
"type": "object",
|
|
170
|
+
"properties": {
|
|
171
|
+
"type": {"type": "string"},
|
|
172
|
+
"label": {"type": "string"},
|
|
173
|
+
"required": {"type": "string"},
|
|
174
|
+
"help_text": {"type": "string"},
|
|
175
|
+
"choices": {"type": "array", "items": {"type": "array"}},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
@action(detail=False, url_path="table-fields", methods=["get"])
|
|
182
|
+
def table_fields(self, request):
|
|
183
|
+
table = get_table_for_model(self.queryset.model)
|
|
184
|
+
table_instance = table(user=request.user, data=[])
|
|
185
|
+
data = [
|
|
186
|
+
{"name": item[0], "label": item[1]}
|
|
187
|
+
for item in table_instance.configurable_columns
|
|
188
|
+
if item[0] in table_instance.visible_columns
|
|
189
|
+
]
|
|
190
|
+
return Response({"data": data})
|
|
191
|
+
|
|
192
|
+
|
|
115
193
|
#
|
|
116
194
|
# Computed Fields
|
|
117
195
|
#
|
|
@@ -155,20 +233,20 @@ class ConfigContextQuerySetMixin:
|
|
|
155
233
|
data annotation or not.
|
|
156
234
|
"""
|
|
157
235
|
|
|
158
|
-
filter_backends = [ConfigContextFilterBackend]
|
|
236
|
+
filter_backends = [ConfigContextFilterBackend, OrderingFilter]
|
|
159
237
|
|
|
160
238
|
def get_queryset(self):
|
|
161
239
|
"""
|
|
162
240
|
Build the proper queryset based on the request context
|
|
163
241
|
|
|
164
|
-
If the `
|
|
242
|
+
If the `exclude` query param
|
|
165
243
|
includes `config_context` as a value, return the base queryset.
|
|
166
244
|
|
|
167
245
|
Else, return the queryset annotated with config context data
|
|
168
246
|
"""
|
|
169
247
|
queryset = super().get_queryset()
|
|
170
248
|
request = self.get_serializer_context()["request"]
|
|
171
|
-
if
|
|
249
|
+
if request is not None and "config_context" in request.query_params.get("exclude", []):
|
|
172
250
|
return queryset
|
|
173
251
|
return queryset.annotate_config_context_data()
|
|
174
252
|
|
|
@@ -236,7 +314,6 @@ class CustomFieldModelViewSet(ModelViewSet):
|
|
|
236
314
|
"""
|
|
237
315
|
|
|
238
316
|
def get_serializer_context(self):
|
|
239
|
-
|
|
240
317
|
# Gather all custom fields for the model
|
|
241
318
|
content_type = ContentType.objects.get_for_model(self.queryset.model)
|
|
242
319
|
custom_fields = content_type.custom_fields.all()
|
|
@@ -250,12 +327,23 @@ class CustomFieldModelViewSet(ModelViewSet):
|
|
|
250
327
|
return context
|
|
251
328
|
|
|
252
329
|
|
|
253
|
-
class NautobotModelViewSet(CustomFieldModelViewSet, NotesViewSetMixin):
|
|
330
|
+
class NautobotModelViewSet(CustomFieldModelViewSet, NotesViewSetMixin, FormFieldsViewSetMixin, TableFieldsViewSetMixin):
|
|
254
331
|
"""Base class to use for API ViewSets based on OrganizationalModel or PrimaryModel.
|
|
255
332
|
|
|
256
333
|
Can also be used for models derived from BaseModel, so long as they support Notes.
|
|
257
334
|
"""
|
|
258
335
|
|
|
336
|
+
# TODO: this currently throws a 500 error in drf_react_template because it's returning an HttpResponse but
|
|
337
|
+
# drf_react_template thinks it's a REST endpoint that should be returning a JsonResponse
|
|
338
|
+
@action(detail=True, url_path="app_full_width_fragment")
|
|
339
|
+
def app_full_width_fragment(self, request, pk):
|
|
340
|
+
"""
|
|
341
|
+
Return html fragment from a plugin.
|
|
342
|
+
"""
|
|
343
|
+
obj = get_object_or_404(self.queryset, pk=pk)
|
|
344
|
+
|
|
345
|
+
return render(request, "generic/object_retrieve_plugin_full_width.html", {"object": obj})
|
|
346
|
+
|
|
259
347
|
|
|
260
348
|
#
|
|
261
349
|
# Custom Links
|
|
@@ -350,7 +438,7 @@ class GitRepositoryViewSet(NautobotModelViewSet):
|
|
|
350
438
|
raise CeleryWorkerNotRunningException()
|
|
351
439
|
|
|
352
440
|
repository = get_object_or_404(GitRepository, id=pk)
|
|
353
|
-
|
|
441
|
+
repository.sync(user=request.user)
|
|
354
442
|
return Response({"message": f"Repository {repository} sync job added to queue."})
|
|
355
443
|
|
|
356
444
|
|
|
@@ -373,7 +461,7 @@ class GraphQLQueryViewSet(ModelViewSet, NotesViewSetMixin):
|
|
|
373
461
|
def run(self, request, pk):
|
|
374
462
|
try:
|
|
375
463
|
query = get_object_or_404(self.queryset, pk=pk)
|
|
376
|
-
result = execute_saved_query(query.
|
|
464
|
+
result = execute_saved_query(query.name, variables=request.data.get("variables"), request=request).to_dict()
|
|
377
465
|
return Response(result)
|
|
378
466
|
except GraphQLError as error:
|
|
379
467
|
return Response(
|
|
@@ -398,25 +486,16 @@ class ImageAttachmentViewSet(ModelViewSet):
|
|
|
398
486
|
#
|
|
399
487
|
|
|
400
488
|
|
|
401
|
-
def _create_schedule(serializer, data,
|
|
489
|
+
def _create_schedule(serializer, data, job_model, user, approval_required, task_queue=None):
|
|
402
490
|
"""
|
|
403
491
|
This is an internal function to create a scheduled job from API data.
|
|
404
492
|
It has to handle both once-offs (i.e. of type TYPE_FUTURE) and interval
|
|
405
493
|
jobs.
|
|
406
494
|
"""
|
|
407
|
-
task_kwargs = {
|
|
408
|
-
"data": data,
|
|
409
|
-
"request": copy_safe_request(request),
|
|
410
|
-
"user": request.user.pk,
|
|
411
|
-
"commit": commit,
|
|
412
|
-
"name": job.class_path,
|
|
413
|
-
"celery_kwargs": celery_kwargs,
|
|
414
|
-
"task_queue": task_queue,
|
|
415
|
-
}
|
|
416
495
|
type_ = serializer["interval"]
|
|
417
496
|
if type_ == JobExecutionType.TYPE_IMMEDIATELY:
|
|
418
497
|
time = timezone.now()
|
|
419
|
-
name = serializer.get("name") or f"{
|
|
498
|
+
name = serializer.get("name") or f"{job_model.name} - {time}"
|
|
420
499
|
elif type_ == JobExecutionType.TYPE_CUSTOM:
|
|
421
500
|
time = serializer.get("start_time") # doing .get("key", "default") returns None instead of "default"
|
|
422
501
|
if time is None:
|
|
@@ -429,6 +508,11 @@ def _create_schedule(serializer, data, commit, job, job_model, request, celery_k
|
|
|
429
508
|
name = serializer["name"]
|
|
430
509
|
crontab = serializer.get("crontab", "")
|
|
431
510
|
|
|
511
|
+
celery_kwargs = {
|
|
512
|
+
"nautobot_job_profile": False,
|
|
513
|
+
"queue": task_queue,
|
|
514
|
+
}
|
|
515
|
+
|
|
432
516
|
# 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
|
|
433
517
|
#
|
|
434
518
|
# We pass in job_class and job_model here partly for forward/backward compatibility logic, and
|
|
@@ -438,16 +522,17 @@ def _create_schedule(serializer, data, commit, job, job_model, request, celery_k
|
|
|
438
522
|
# scheduled for.
|
|
439
523
|
scheduled_job = ScheduledJob(
|
|
440
524
|
name=name,
|
|
441
|
-
task=
|
|
442
|
-
job_class=
|
|
525
|
+
task=job_model.job_class.registered_name,
|
|
526
|
+
job_class=job_model.class_path,
|
|
443
527
|
job_model=job_model,
|
|
444
528
|
start_time=time,
|
|
445
|
-
description=f"Nautobot job {name} scheduled by {
|
|
446
|
-
kwargs=
|
|
529
|
+
description=f"Nautobot job {name} scheduled by {user} for {time}",
|
|
530
|
+
kwargs=data,
|
|
531
|
+
celery_kwargs=celery_kwargs,
|
|
447
532
|
interval=type_,
|
|
448
533
|
one_off=(type_ == JobExecutionType.TYPE_FUTURE),
|
|
449
|
-
user=
|
|
450
|
-
approval_required=
|
|
534
|
+
user=user,
|
|
535
|
+
approval_required=approval_required,
|
|
451
536
|
crontab=crontab,
|
|
452
537
|
queue=task_queue,
|
|
453
538
|
)
|
|
@@ -455,178 +540,6 @@ def _create_schedule(serializer, data, commit, job, job_model, request, celery_k
|
|
|
455
540
|
return scheduled_job
|
|
456
541
|
|
|
457
542
|
|
|
458
|
-
def _run_job(request, job_model, legacy_response=False):
|
|
459
|
-
"""An internal function providing logic shared between JobModelViewSet.run() and JobViewSet.run()."""
|
|
460
|
-
if not request.user.has_perm("extras.run_job"):
|
|
461
|
-
raise PermissionDenied("This user does not have permission to run jobs.")
|
|
462
|
-
if not job_model.enabled:
|
|
463
|
-
raise PermissionDenied("This job is not enabled to be run.")
|
|
464
|
-
if not job_model.installed:
|
|
465
|
-
raise MethodNotAllowed(request.method, detail="This job is not presently installed and cannot be run")
|
|
466
|
-
if job_model.has_sensitive_variables:
|
|
467
|
-
if (
|
|
468
|
-
"schedule" in request.data
|
|
469
|
-
and "interval" in request.data["schedule"]
|
|
470
|
-
and request.data["schedule"]["interval"] != JobExecutionType.TYPE_IMMEDIATELY
|
|
471
|
-
):
|
|
472
|
-
raise ValidationError(
|
|
473
|
-
{"schedule": {"interval": ["Unable to schedule job: Job may have sensitive input variables"]}}
|
|
474
|
-
)
|
|
475
|
-
if job_model.approval_required:
|
|
476
|
-
raise ValidationError(
|
|
477
|
-
"Unable to run or schedule job: "
|
|
478
|
-
"This job is flagged as possibly having sensitive variables but is also flagged as requiring approval."
|
|
479
|
-
"One of these two flags must be removed before this job can be scheduled or run."
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
job_class = job_model.job_class
|
|
483
|
-
if job_class is None:
|
|
484
|
-
raise MethodNotAllowed(request.method, detail="This job's source code could not be located and cannot be run")
|
|
485
|
-
job = job_class()
|
|
486
|
-
|
|
487
|
-
valid_queues = job_model.task_queues if job_model.task_queues else [settings.CELERY_TASK_DEFAULT_QUEUE]
|
|
488
|
-
# Get a default queue from either the job model's specified task queue or system default to fall back on if request doesn't provide one
|
|
489
|
-
default_valid_queue = valid_queues[0]
|
|
490
|
-
|
|
491
|
-
# We need to call request.data for both cases as this is what pulls and caches the request data
|
|
492
|
-
data = request.data
|
|
493
|
-
files = None
|
|
494
|
-
schedule_data = None
|
|
495
|
-
|
|
496
|
-
# We must extract from the request:
|
|
497
|
-
# - Job Form data (for submission to the job itself)
|
|
498
|
-
# - Schedule data
|
|
499
|
-
# - Commit flag state
|
|
500
|
-
# - Desired task queue
|
|
501
|
-
# Depending on request content type (largely for backwards compatibility) the keys at which these are found are different
|
|
502
|
-
if "multipart/form-data" in request.content_type:
|
|
503
|
-
data = request._data.dict() # .data will return data and files, we just want the data
|
|
504
|
-
files = request.FILES
|
|
505
|
-
|
|
506
|
-
# JobMultiPartInputSerializer is a "flattened" version of JobInputSerializer
|
|
507
|
-
input_serializer = serializers.JobMultiPartInputSerializer(data=data, context={"request": request})
|
|
508
|
-
input_serializer.is_valid(raise_exception=True)
|
|
509
|
-
|
|
510
|
-
commit = input_serializer.validated_data.get("_commit", None)
|
|
511
|
-
task_queue = input_serializer.validated_data.get("_task_queue", default_valid_queue)
|
|
512
|
-
|
|
513
|
-
# JobMultiPartInputSerializer only has keys for executing job (commit, task_queue, etc),
|
|
514
|
-
# everything else is a candidate for the job form's data.
|
|
515
|
-
# job_class.validate_data will throw an error for any unexpected key/value pairs.
|
|
516
|
-
non_job_keys = input_serializer.validated_data.keys()
|
|
517
|
-
for non_job_key in non_job_keys:
|
|
518
|
-
data.pop(non_job_key, None)
|
|
519
|
-
|
|
520
|
-
# List of keys in serializer that are effectively exploded versions of the schedule dictionary from JobInputSerializer
|
|
521
|
-
schedule_keys = ("_schedule_name", "_schedule_start_time", "_schedule_interval", "_schedule_crontab")
|
|
522
|
-
|
|
523
|
-
# Assign the key from the validated_data output to dictionary without prefixed "_schedule_"
|
|
524
|
-
# For all the keys that are schedule keys
|
|
525
|
-
# Assign only if the key is in the output since we don't want None's if not provided
|
|
526
|
-
if any(schedule_key in non_job_keys for schedule_key in schedule_keys):
|
|
527
|
-
schedule_data = {
|
|
528
|
-
k.replace("_schedule_", ""): input_serializer.validated_data[k]
|
|
529
|
-
for k in schedule_keys
|
|
530
|
-
if k in input_serializer.validated_data
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
else:
|
|
534
|
-
input_serializer = serializers.JobInputSerializer(data=data, context={"request": request})
|
|
535
|
-
input_serializer.is_valid(raise_exception=True)
|
|
536
|
-
|
|
537
|
-
data = input_serializer.validated_data.get("data", {})
|
|
538
|
-
commit = input_serializer.validated_data.get("commit", None)
|
|
539
|
-
task_queue = input_serializer.validated_data.get("task_queue", default_valid_queue)
|
|
540
|
-
schedule_data = input_serializer.validated_data.get("schedule", None)
|
|
541
|
-
|
|
542
|
-
if commit is None:
|
|
543
|
-
commit = job_model.commit_default
|
|
544
|
-
|
|
545
|
-
if task_queue not in valid_queues:
|
|
546
|
-
raise ValidationError({"task_queue": [f'"{task_queue}" is not a valid choice.']})
|
|
547
|
-
|
|
548
|
-
cleaned_data = None
|
|
549
|
-
try:
|
|
550
|
-
cleaned_data = job.validate_data(data, files=files)
|
|
551
|
-
cleaned_data.pop(
|
|
552
|
-
"_commit", None
|
|
553
|
-
) # We don't get commit from the form, instead it's part of the serializer's validated data
|
|
554
|
-
|
|
555
|
-
except FormsValidationError as e:
|
|
556
|
-
# message_dict can only be accessed if ValidationError got a dict
|
|
557
|
-
# in the constructor (saved as error_dict). Otherwise we get a list
|
|
558
|
-
# of errors under messages
|
|
559
|
-
return Response({"errors": e.message_dict if hasattr(e, "error_dict") else e.messages}, status=400)
|
|
560
|
-
|
|
561
|
-
if not get_worker_count(queue=task_queue):
|
|
562
|
-
raise CeleryWorkerNotRunningException(queue=task_queue)
|
|
563
|
-
|
|
564
|
-
job_content_type = get_job_content_type()
|
|
565
|
-
|
|
566
|
-
# Default to a null JobResult.
|
|
567
|
-
job_result = None
|
|
568
|
-
|
|
569
|
-
# Assert that a job with `approval_required=True` has a schedule that enforces approval and
|
|
570
|
-
# executes immediately.
|
|
571
|
-
if schedule_data is None and job_model.approval_required:
|
|
572
|
-
schedule_data = {"interval": JobExecutionType.TYPE_IMMEDIATELY}
|
|
573
|
-
|
|
574
|
-
# Skip creating a ScheduledJob when job can be executed immediately
|
|
575
|
-
elif (
|
|
576
|
-
schedule_data
|
|
577
|
-
and schedule_data["interval"] == JobExecutionType.TYPE_IMMEDIATELY
|
|
578
|
-
and not job_model.approval_required
|
|
579
|
-
):
|
|
580
|
-
schedule_data = None
|
|
581
|
-
|
|
582
|
-
# Try to create a ScheduledJob, or...
|
|
583
|
-
if schedule_data:
|
|
584
|
-
schedule = _create_schedule(
|
|
585
|
-
schedule_data,
|
|
586
|
-
job_class.serialize_data(cleaned_data),
|
|
587
|
-
commit,
|
|
588
|
-
job,
|
|
589
|
-
job_model,
|
|
590
|
-
request,
|
|
591
|
-
celery_kwargs={"queue": task_queue},
|
|
592
|
-
task_queue=input_serializer.validated_data.get("task_queue", None),
|
|
593
|
-
)
|
|
594
|
-
else:
|
|
595
|
-
schedule = None
|
|
596
|
-
|
|
597
|
-
# ... If we can't create one, create a JobResult instead.
|
|
598
|
-
if schedule is None:
|
|
599
|
-
job_result = JobResult.enqueue_job(
|
|
600
|
-
run_job,
|
|
601
|
-
job.class_path,
|
|
602
|
-
job_content_type,
|
|
603
|
-
request.user,
|
|
604
|
-
celery_kwargs={"queue": task_queue},
|
|
605
|
-
data=job_class.serialize_data(cleaned_data),
|
|
606
|
-
request=copy_safe_request(request),
|
|
607
|
-
commit=commit,
|
|
608
|
-
task_queue=input_serializer.validated_data.get("task_queue", None),
|
|
609
|
-
)
|
|
610
|
-
job.result = job_result
|
|
611
|
-
|
|
612
|
-
if legacy_response:
|
|
613
|
-
# Old-style JobViewSet response - serialize the Job class in the response for some reason?
|
|
614
|
-
serializer = serializers.JobClassDetailSerializer(job, context={"request": request})
|
|
615
|
-
return Response(serializer.data)
|
|
616
|
-
else:
|
|
617
|
-
# New-style JobModelViewSet response - serialize the schedule or job_result as appropriate
|
|
618
|
-
data = {"scheduled_job": None, "job_result": None}
|
|
619
|
-
if schedule:
|
|
620
|
-
data["scheduled_job"] = nested_serializers.NestedScheduledJobSerializer(
|
|
621
|
-
schedule, context={"request": request}
|
|
622
|
-
).data
|
|
623
|
-
if job_result:
|
|
624
|
-
data["job_result"] = nested_serializers.NestedJobResultSerializer(
|
|
625
|
-
job_result, context={"request": request}
|
|
626
|
-
).data
|
|
627
|
-
return Response(data, status=status.HTTP_201_CREATED)
|
|
628
|
-
|
|
629
|
-
|
|
630
543
|
class JobViewSet(
|
|
631
544
|
# DRF mixins:
|
|
632
545
|
# note no CreateModelMixin
|
|
@@ -643,43 +556,6 @@ class JobViewSet(
|
|
|
643
556
|
serializer_class = serializers.JobSerializer
|
|
644
557
|
filterset_class = filters.JobFilterSet
|
|
645
558
|
|
|
646
|
-
@extend_schema(
|
|
647
|
-
deprecated=True,
|
|
648
|
-
operation_id="extras_jobs_read_deprecated",
|
|
649
|
-
responses={"200": serializers.JobClassDetailSerializer()},
|
|
650
|
-
)
|
|
651
|
-
@action(
|
|
652
|
-
detail=False, # a /jobs/... URL, not a /jobs/<pk>/... URL
|
|
653
|
-
methods=["get"],
|
|
654
|
-
url_path="(?P<class_path>[^/]+/[^/]+/[^/]+)", # /api/extras/jobs/<class_path>/
|
|
655
|
-
url_name="detail",
|
|
656
|
-
)
|
|
657
|
-
def retrieve_deprecated(self, request, class_path):
|
|
658
|
-
"""
|
|
659
|
-
Get details of a Job as identified by its class-path.
|
|
660
|
-
|
|
661
|
-
This API endpoint is deprecated; it is recommended to use the extras_jobs_read endpoint instead.
|
|
662
|
-
"""
|
|
663
|
-
if not request.user.has_perm("extras.view_job"):
|
|
664
|
-
raise PermissionDenied("This user does not have permission to view jobs.")
|
|
665
|
-
try:
|
|
666
|
-
job_model = Job.objects.restrict(request.user, "view").get_for_class_path(class_path)
|
|
667
|
-
except Job.DoesNotExist:
|
|
668
|
-
raise Http404
|
|
669
|
-
if not job_model.installed or job_model.job_class is None:
|
|
670
|
-
raise Http404
|
|
671
|
-
job_content_type = get_job_content_type()
|
|
672
|
-
job = job_model.job_class() # TODO: why do we need to instantiate the job_class?
|
|
673
|
-
job.result = JobResult.objects.filter(
|
|
674
|
-
obj_type=job_content_type,
|
|
675
|
-
name=job.class_path,
|
|
676
|
-
status__in=JobResultStatusChoices.READY_STATES,
|
|
677
|
-
).first()
|
|
678
|
-
|
|
679
|
-
serializer = serializers.JobClassDetailSerializer(job, context={"request": request})
|
|
680
|
-
|
|
681
|
-
return Response(serializer.data)
|
|
682
|
-
|
|
683
559
|
@extend_schema(responses={"200": serializers.JobVariableSerializer(many=True)})
|
|
684
560
|
@action(detail=True, filterset_class=None)
|
|
685
561
|
def variables(self, request, pk):
|
|
@@ -745,36 +621,150 @@ class JobViewSet(
|
|
|
745
621
|
def run(self, request, *args, pk, **kwargs):
|
|
746
622
|
"""Run the specified Job."""
|
|
747
623
|
job_model = self.get_object()
|
|
748
|
-
return _run_job(request, job_model)
|
|
749
|
-
|
|
750
|
-
@extend_schema(
|
|
751
|
-
deprecated=True,
|
|
752
|
-
methods=["post"],
|
|
753
|
-
request=serializers.JobInputSerializer,
|
|
754
|
-
responses={"200": serializers.JobClassDetailSerializer()},
|
|
755
|
-
operation_id="extras_jobs_run_deprecated",
|
|
756
|
-
)
|
|
757
|
-
@action(
|
|
758
|
-
detail=False, # a /jobs/... URL, not a /jobs/<pk>/... URL
|
|
759
|
-
methods=["post"],
|
|
760
|
-
permission_classes=[JobRunTokenPermissions],
|
|
761
|
-
url_path="(?P<class_path>[^/]+/[^/]+/[^/]+)/run", # /api/extras/jobs/<class_path>/run/
|
|
762
|
-
url_name="run",
|
|
763
|
-
parser_classes=[JSONParser, MultiPartParser],
|
|
764
|
-
)
|
|
765
|
-
def run_deprecated(self, request, class_path):
|
|
766
|
-
"""
|
|
767
|
-
Run a Job as identified by its class-path.
|
|
768
|
-
|
|
769
|
-
This API endpoint is deprecated; it is recommended to use the extras_jobs_run endpoint instead.
|
|
770
|
-
"""
|
|
771
624
|
if not request.user.has_perm("extras.run_job"):
|
|
772
625
|
raise PermissionDenied("This user does not have permission to run jobs.")
|
|
626
|
+
if not job_model.enabled:
|
|
627
|
+
raise PermissionDenied("This job is not enabled to be run.")
|
|
628
|
+
if not job_model.installed:
|
|
629
|
+
raise MethodNotAllowed(request.method, detail="This job is not presently installed and cannot be run")
|
|
630
|
+
if job_model.has_sensitive_variables:
|
|
631
|
+
if (
|
|
632
|
+
"schedule" in request.data
|
|
633
|
+
and "interval" in request.data["schedule"]
|
|
634
|
+
and request.data["schedule"]["interval"] != JobExecutionType.TYPE_IMMEDIATELY
|
|
635
|
+
):
|
|
636
|
+
raise ValidationError(
|
|
637
|
+
{"schedule": {"interval": ["Unable to schedule job: Job may have sensitive input variables"]}}
|
|
638
|
+
)
|
|
639
|
+
if job_model.approval_required:
|
|
640
|
+
raise ValidationError(
|
|
641
|
+
"Unable to run or schedule job: "
|
|
642
|
+
"This job is flagged as possibly having sensitive variables but is also flagged as requiring approval."
|
|
643
|
+
"One of these two flags must be removed before this job can be scheduled or run."
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
job_class = job_model.job_class
|
|
647
|
+
if job_class is None:
|
|
648
|
+
raise MethodNotAllowed(
|
|
649
|
+
request.method, detail="This job's source code could not be located and cannot be run"
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
valid_queues = job_model.task_queues if job_model.task_queues else [settings.CELERY_TASK_DEFAULT_QUEUE]
|
|
653
|
+
# Get a default queue from either the job model's specified task queue or system default to fall back on if request doesn't provide one
|
|
654
|
+
default_valid_queue = valid_queues[0]
|
|
655
|
+
|
|
656
|
+
# We need to call request.data for both cases as this is what pulls and caches the request data
|
|
657
|
+
data = request.data
|
|
658
|
+
files = None
|
|
659
|
+
schedule_data = None
|
|
660
|
+
|
|
661
|
+
# We must extract from the request:
|
|
662
|
+
# - Job Form data (for submission to the job itself)
|
|
663
|
+
# - Schedule data
|
|
664
|
+
# - Desired task queue
|
|
665
|
+
# Depending on request content type (largely for backwards compatibility) the keys at which these are found are different
|
|
666
|
+
if "multipart/form-data" in request.content_type:
|
|
667
|
+
data = request._data.dict() # .data will return data and files, we just want the data
|
|
668
|
+
files = request.FILES
|
|
669
|
+
|
|
670
|
+
# JobMultiPartInputSerializer is a "flattened" version of JobInputSerializer
|
|
671
|
+
input_serializer = serializers.JobMultiPartInputSerializer(data=data, context={"request": request})
|
|
672
|
+
input_serializer.is_valid(raise_exception=True)
|
|
673
|
+
|
|
674
|
+
task_queue = input_serializer.validated_data.get("_task_queue", default_valid_queue)
|
|
675
|
+
|
|
676
|
+
# JobMultiPartInputSerializer only has keys for executing job (task_queue, etc),
|
|
677
|
+
# everything else is a candidate for the job form's data.
|
|
678
|
+
# job_class.validate_data will throw an error for any unexpected key/value pairs.
|
|
679
|
+
non_job_keys = input_serializer.validated_data.keys()
|
|
680
|
+
for non_job_key in non_job_keys:
|
|
681
|
+
data.pop(non_job_key, None)
|
|
682
|
+
|
|
683
|
+
# List of keys in serializer that are effectively exploded versions of the schedule dictionary from JobInputSerializer
|
|
684
|
+
schedule_keys = ("_schedule_name", "_schedule_start_time", "_schedule_interval", "_schedule_crontab")
|
|
685
|
+
|
|
686
|
+
# Assign the key from the validated_data output to dictionary without prefixed "_schedule_"
|
|
687
|
+
# For all the keys that are schedule keys
|
|
688
|
+
# Assign only if the key is in the output since we don't want None's if not provided
|
|
689
|
+
if any(schedule_key in non_job_keys for schedule_key in schedule_keys):
|
|
690
|
+
schedule_data = {
|
|
691
|
+
k.replace("_schedule_", ""): input_serializer.validated_data[k]
|
|
692
|
+
for k in schedule_keys
|
|
693
|
+
if k in input_serializer.validated_data
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
else:
|
|
697
|
+
input_serializer = serializers.JobInputSerializer(data=data, context={"request": request})
|
|
698
|
+
input_serializer.is_valid(raise_exception=True)
|
|
699
|
+
|
|
700
|
+
data = input_serializer.validated_data.get("data", {})
|
|
701
|
+
task_queue = input_serializer.validated_data.get("task_queue", default_valid_queue)
|
|
702
|
+
schedule_data = input_serializer.validated_data.get("schedule", None)
|
|
703
|
+
|
|
704
|
+
if task_queue not in valid_queues:
|
|
705
|
+
raise ValidationError({"task_queue": [f'"{task_queue}" is not a valid choice.']})
|
|
706
|
+
|
|
707
|
+
cleaned_data = None
|
|
773
708
|
try:
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
709
|
+
cleaned_data = job_class().validate_data(data, files=files)
|
|
710
|
+
cleaned_data = job_model.job_class.prepare_job_kwargs(cleaned_data)
|
|
711
|
+
|
|
712
|
+
except FormsValidationError as e:
|
|
713
|
+
# message_dict can only be accessed if ValidationError got a dict
|
|
714
|
+
# in the constructor (saved as error_dict). Otherwise we get a list
|
|
715
|
+
# of errors under messages
|
|
716
|
+
return Response({"errors": e.message_dict if hasattr(e, "error_dict") else e.messages}, status=400)
|
|
717
|
+
|
|
718
|
+
if not get_worker_count(queue=task_queue):
|
|
719
|
+
raise CeleryWorkerNotRunningException(queue=task_queue)
|
|
720
|
+
|
|
721
|
+
# Default to a null JobResult.
|
|
722
|
+
job_result = None
|
|
723
|
+
|
|
724
|
+
# Approval is not required for dryrun
|
|
725
|
+
if job_class.supports_dryrun:
|
|
726
|
+
dryrun = data.get("dryrun", False)
|
|
727
|
+
approval_required = not dryrun and job_model.approval_required
|
|
728
|
+
else:
|
|
729
|
+
approval_required = job_model.approval_required
|
|
730
|
+
|
|
731
|
+
# Set schedule for jobs that require approval but request did not supply schedule data
|
|
732
|
+
if schedule_data is None and approval_required:
|
|
733
|
+
schedule_data = {"interval": JobExecutionType.TYPE_IMMEDIATELY}
|
|
734
|
+
|
|
735
|
+
# Skip creating a ScheduledJob when job can be executed immediately
|
|
736
|
+
elif schedule_data and schedule_data["interval"] == JobExecutionType.TYPE_IMMEDIATELY and not approval_required:
|
|
737
|
+
schedule_data = None
|
|
738
|
+
|
|
739
|
+
# Try to create a ScheduledJob, or...
|
|
740
|
+
if schedule_data:
|
|
741
|
+
schedule = _create_schedule(
|
|
742
|
+
schedule_data,
|
|
743
|
+
job_class.serialize_data(cleaned_data),
|
|
744
|
+
job_model,
|
|
745
|
+
request.user,
|
|
746
|
+
approval_required,
|
|
747
|
+
task_queue=input_serializer.validated_data.get("task_queue", None),
|
|
748
|
+
)
|
|
749
|
+
else:
|
|
750
|
+
schedule = None
|
|
751
|
+
|
|
752
|
+
# ... If we can't create one, create a JobResult instead.
|
|
753
|
+
if schedule is None:
|
|
754
|
+
job_result = JobResult.enqueue_job(
|
|
755
|
+
job_model,
|
|
756
|
+
request.user,
|
|
757
|
+
task_queue=task_queue,
|
|
758
|
+
**job_class.serialize_data(cleaned_data),
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# New-style JobModelViewSet response - serialize the schedule or job_result as appropriate
|
|
762
|
+
data = {"scheduled_job": None, "job_result": None}
|
|
763
|
+
if schedule:
|
|
764
|
+
data["scheduled_job"] = serializers.ScheduledJobSerializer(schedule, context={"request": request}).data
|
|
765
|
+
if job_result:
|
|
766
|
+
data["job_result"] = serializers.JobResultSerializer(job_result, context={"request": request}).data
|
|
767
|
+
return Response(data, status=status.HTTP_201_CREATED)
|
|
778
768
|
|
|
779
769
|
|
|
780
770
|
#
|
|
@@ -820,7 +810,7 @@ class JobResultViewSet(
|
|
|
820
810
|
Retrieve a list of job results
|
|
821
811
|
"""
|
|
822
812
|
|
|
823
|
-
queryset = JobResult.objects.select_related("job_model", "
|
|
813
|
+
queryset = JobResult.objects.select_related("job_model", "user")
|
|
824
814
|
serializer_class = serializers.JobResultSerializer
|
|
825
815
|
filterset_class = filters.JobResultFilterSet
|
|
826
816
|
|
|
@@ -828,7 +818,7 @@ class JobResultViewSet(
|
|
|
828
818
|
def logs(self, request, pk=None):
|
|
829
819
|
job_result = self.get_object()
|
|
830
820
|
logs = job_result.job_log_entries.all()
|
|
831
|
-
serializer =
|
|
821
|
+
serializer = serializers.JobLogEntrySerializer(logs, context={"request": request}, many=True)
|
|
832
822
|
return Response(serializer.data)
|
|
833
823
|
|
|
834
824
|
|
|
@@ -968,21 +958,19 @@ class ScheduledJobViewSet(ReadOnlyModelViewSet):
|
|
|
968
958
|
job_model = scheduled_job.job_model
|
|
969
959
|
if job_model is None or not job_model.runnable:
|
|
970
960
|
raise MethodNotAllowed("This job cannot be dry-run at this time.")
|
|
961
|
+
if not job_model.supports_dryrun:
|
|
962
|
+
raise MethodNotAllowed("This job does not support dry-run.")
|
|
971
963
|
if not Job.objects.check_perms(request.user, instance=job_model, action="run"):
|
|
972
964
|
raise PermissionDenied("You do not have permission to run this job.")
|
|
973
965
|
|
|
974
|
-
# Immediately enqueue the job
|
|
975
|
-
|
|
966
|
+
# Immediately enqueue the job
|
|
967
|
+
job_kwargs = job_model.job_class.prepare_job_kwargs(scheduled_job.kwargs.get("data", {}))
|
|
968
|
+
job_kwargs["dryrun"] = True
|
|
976
969
|
job_result = JobResult.enqueue_job(
|
|
977
|
-
|
|
978
|
-
job_model.class_path,
|
|
979
|
-
job_content_type,
|
|
970
|
+
job_model,
|
|
980
971
|
request.user,
|
|
981
|
-
celery_kwargs=scheduled_job.
|
|
982
|
-
|
|
983
|
-
request=copy_safe_request(request),
|
|
984
|
-
commit=False, # force a dry-run
|
|
985
|
-
task_queue=scheduled_job.kwargs.get("task_queue", None),
|
|
972
|
+
celery_kwargs=scheduled_job.celery_kwargs or {},
|
|
973
|
+
**job_model.job_class.serialize_data(job_kwargs),
|
|
986
974
|
)
|
|
987
975
|
serializer = serializers.JobResultSerializer(job_result, context={"request": request})
|
|
988
976
|
|
|
@@ -994,7 +982,7 @@ class ScheduledJobViewSet(ReadOnlyModelViewSet):
|
|
|
994
982
|
#
|
|
995
983
|
|
|
996
984
|
|
|
997
|
-
class NoteViewSet(ModelViewSet):
|
|
985
|
+
class NoteViewSet(ModelViewSet, TableFieldsViewSetMixin):
|
|
998
986
|
queryset = Note.objects.select_related("user")
|
|
999
987
|
serializer_class = serializers.NoteSerializer
|
|
1000
988
|
filterset_class = filters.NoteFilterSet
|
|
@@ -1009,7 +997,7 @@ class NoteViewSet(ModelViewSet):
|
|
|
1009
997
|
#
|
|
1010
998
|
|
|
1011
999
|
|
|
1012
|
-
class ObjectChangeViewSet(ReadOnlyModelViewSet):
|
|
1000
|
+
class ObjectChangeViewSet(ReadOnlyModelViewSet, TableFieldsViewSetMixin):
|
|
1013
1001
|
"""
|
|
1014
1002
|
Retrieve a list of recent changes.
|
|
1015
1003
|
"""
|
|
@@ -1061,6 +1049,31 @@ class SecretsViewSet(NautobotModelViewSet):
|
|
|
1061
1049
|
serializer_class = serializers.SecretSerializer
|
|
1062
1050
|
filterset_class = filters.SecretFilterSet
|
|
1063
1051
|
|
|
1052
|
+
@extend_schema(
|
|
1053
|
+
responses={
|
|
1054
|
+
200: {
|
|
1055
|
+
"type": "object",
|
|
1056
|
+
"properties": {
|
|
1057
|
+
"result": {"type": "boolean"},
|
|
1058
|
+
"message": {"type": "string"},
|
|
1059
|
+
},
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
)
|
|
1063
|
+
@action(methods=["GET"], detail=True)
|
|
1064
|
+
def check(self, request, pk):
|
|
1065
|
+
"""Check that a secret's value is accessible."""
|
|
1066
|
+
result = False
|
|
1067
|
+
message = "Unknown error"
|
|
1068
|
+
try:
|
|
1069
|
+
self.get_object().get_value()
|
|
1070
|
+
result = True
|
|
1071
|
+
message = "Passed"
|
|
1072
|
+
except SecretError as e:
|
|
1073
|
+
message = str(e)
|
|
1074
|
+
response = {"result": result, "message": message}
|
|
1075
|
+
return Response(response)
|
|
1076
|
+
|
|
1064
1077
|
|
|
1065
1078
|
class SecretsGroupViewSet(NautobotModelViewSet):
|
|
1066
1079
|
"""
|