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/models/jobs.py
CHANGED
|
@@ -1,33 +1,38 @@
|
|
|
1
1
|
# Data models relating to Jobs
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from datetime import timedelta
|
|
4
5
|
import logging
|
|
5
|
-
import os
|
|
6
|
-
import uuid
|
|
7
6
|
|
|
8
7
|
from celery import schedules
|
|
8
|
+
from celery.utils.log import get_logger, LoggingProxy
|
|
9
9
|
from django.conf import settings
|
|
10
10
|
from django.contrib.contenttypes.models import ContentType
|
|
11
|
-
from django.core.exceptions import
|
|
11
|
+
from django.core.exceptions import ValidationError
|
|
12
12
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
13
13
|
from django.core.validators import MinValueValidator
|
|
14
14
|
from django.db import models, transaction
|
|
15
15
|
from django.db.models import signals
|
|
16
|
-
from django.urls import reverse
|
|
17
16
|
from django.utils import timezone
|
|
17
|
+
from django.utils.functional import cached_property
|
|
18
18
|
from django_celery_beat.clockedschedule import clocked
|
|
19
19
|
from prometheus_client import Histogram
|
|
20
20
|
|
|
21
|
-
from nautobot.core.celery import
|
|
21
|
+
from nautobot.core.celery import (
|
|
22
|
+
add_nautobot_log_handler,
|
|
23
|
+
app,
|
|
24
|
+
NautobotKombuJSONEncoder,
|
|
25
|
+
setup_nautobot_job_logging,
|
|
26
|
+
)
|
|
27
|
+
from nautobot.core.celery.control import refresh_git_repository
|
|
22
28
|
from nautobot.core.models import BaseManager, BaseModel
|
|
23
|
-
from nautobot.core.models.fields import
|
|
29
|
+
from nautobot.core.models.fields import JSONArrayField
|
|
24
30
|
from nautobot.core.models.generics import OrganizationalModel, PrimaryModel
|
|
25
31
|
from nautobot.core.utils.logging import sanitize
|
|
26
32
|
from nautobot.extras.choices import (
|
|
27
33
|
ButtonClassChoices,
|
|
28
34
|
JobExecutionType,
|
|
29
35
|
JobResultStatusChoices,
|
|
30
|
-
JobSourceChoices,
|
|
31
36
|
LogLevelChoices,
|
|
32
37
|
)
|
|
33
38
|
from nautobot.extras.constants import (
|
|
@@ -36,20 +41,15 @@ from nautobot.extras.constants import (
|
|
|
36
41
|
JOB_LOG_MAX_LOG_OBJECT_LENGTH,
|
|
37
42
|
JOB_MAX_GROUPING_LENGTH,
|
|
38
43
|
JOB_MAX_NAME_LENGTH,
|
|
39
|
-
JOB_MAX_SOURCE_LENGTH,
|
|
40
44
|
JOB_OVERRIDABLE_FIELDS,
|
|
41
45
|
)
|
|
42
|
-
from nautobot.extras.models import ChangeLoggedModel
|
|
46
|
+
from nautobot.extras.models import ChangeLoggedModel, GitRepository
|
|
43
47
|
from nautobot.extras.models.mixins import NotesMixin
|
|
44
|
-
from nautobot.extras.plugins.utils import import_object
|
|
45
48
|
from nautobot.extras.managers import JobResultManager, ScheduledJobsManager
|
|
46
49
|
from nautobot.extras.querysets import JobQuerySet, ScheduledJobExtendedQuerySet
|
|
47
50
|
from nautobot.extras.utils import (
|
|
48
51
|
ChangeLoggedModelsQuery,
|
|
49
|
-
FeatureQuery,
|
|
50
52
|
extras_features,
|
|
51
|
-
get_job_content_type,
|
|
52
|
-
jobs_in_directory,
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
from .customfields import CustomFieldModel
|
|
@@ -86,23 +86,6 @@ class Job(PrimaryModel):
|
|
|
86
86
|
"""
|
|
87
87
|
|
|
88
88
|
# Information used to locate the Job source code
|
|
89
|
-
source = models.CharField(
|
|
90
|
-
max_length=JOB_MAX_SOURCE_LENGTH,
|
|
91
|
-
choices=JobSourceChoices,
|
|
92
|
-
editable=False,
|
|
93
|
-
db_index=True,
|
|
94
|
-
help_text="Source of the Python code for this job - local, Git repository, or plugins",
|
|
95
|
-
)
|
|
96
|
-
git_repository = models.ForeignKey(
|
|
97
|
-
to="extras.GitRepository",
|
|
98
|
-
blank=True,
|
|
99
|
-
null=True,
|
|
100
|
-
default=None,
|
|
101
|
-
on_delete=models.SET_NULL,
|
|
102
|
-
db_index=True,
|
|
103
|
-
related_name="jobs",
|
|
104
|
-
help_text="Git repository that provides this job",
|
|
105
|
-
)
|
|
106
89
|
module_name = models.CharField(
|
|
107
90
|
max_length=JOB_MAX_NAME_LENGTH,
|
|
108
91
|
editable=False,
|
|
@@ -116,11 +99,6 @@ class Job(PrimaryModel):
|
|
|
116
99
|
help_text="Name of the Python class providing this job",
|
|
117
100
|
)
|
|
118
101
|
|
|
119
|
-
slug = AutoSlugField(
|
|
120
|
-
max_length=JOB_MAX_NAME_LENGTH,
|
|
121
|
-
populate_from="name",
|
|
122
|
-
)
|
|
123
|
-
|
|
124
102
|
# Human-readable information, potentially inherited from the source code
|
|
125
103
|
# See also the docstring of nautobot.extras.jobs.BaseJob.Meta.
|
|
126
104
|
grouping = models.CharField(
|
|
@@ -161,17 +139,17 @@ class Job(PrimaryModel):
|
|
|
161
139
|
approval_required = models.BooleanField(
|
|
162
140
|
default=False, help_text="Whether the job requires approval from another user before running"
|
|
163
141
|
)
|
|
164
|
-
commit_default = models.BooleanField(
|
|
165
|
-
default=True, help_text="Whether the job defaults to committing changes when run, or defaults to a dry-run"
|
|
166
|
-
)
|
|
167
142
|
hidden = models.BooleanField(
|
|
168
143
|
default=False,
|
|
169
144
|
db_index=True,
|
|
170
145
|
help_text="Whether the job defaults to not being shown in the UI",
|
|
171
146
|
)
|
|
172
147
|
# Job.Meta.field_order is not overridable in this model
|
|
148
|
+
dryrun_default = models.BooleanField(
|
|
149
|
+
default=False, help_text="Whether the job defaults to running with dryrun argument set to true"
|
|
150
|
+
)
|
|
173
151
|
read_only = models.BooleanField(
|
|
174
|
-
default=False, help_text="
|
|
152
|
+
default=False, editable=False, help_text="Set to true if the job does not make any changes to the environment"
|
|
175
153
|
)
|
|
176
154
|
soft_time_limit = models.FloatField(
|
|
177
155
|
default=0,
|
|
@@ -191,6 +169,11 @@ class Job(PrimaryModel):
|
|
|
191
169
|
blank=True,
|
|
192
170
|
help_text="Comma separated list of task queues that this job can run on. A blank list will use the default queue",
|
|
193
171
|
)
|
|
172
|
+
supports_dryrun = models.BooleanField(
|
|
173
|
+
default=False,
|
|
174
|
+
editable=False,
|
|
175
|
+
help_text="If supported, allows the job to bypass approval when running with dryrun argument set to true",
|
|
176
|
+
)
|
|
194
177
|
|
|
195
178
|
# Flags to indicate whether the above properties are inherited from the source code or overridden by the database
|
|
196
179
|
grouping_override = models.BooleanField(
|
|
@@ -205,12 +188,11 @@ class Job(PrimaryModel):
|
|
|
205
188
|
default=False,
|
|
206
189
|
help_text="If set, the configured description will remain even if the underlying Job source code changes",
|
|
207
190
|
)
|
|
208
|
-
|
|
209
191
|
approval_required_override = models.BooleanField(
|
|
210
192
|
default=False,
|
|
211
193
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
212
194
|
)
|
|
213
|
-
|
|
195
|
+
dryrun_default_override = models.BooleanField(
|
|
214
196
|
default=False,
|
|
215
197
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
216
198
|
)
|
|
@@ -218,10 +200,6 @@ class Job(PrimaryModel):
|
|
|
218
200
|
default=False,
|
|
219
201
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
220
202
|
)
|
|
221
|
-
read_only_override = models.BooleanField(
|
|
222
|
-
default=False,
|
|
223
|
-
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
224
|
-
)
|
|
225
203
|
soft_time_limit_override = models.BooleanField(
|
|
226
204
|
default=False,
|
|
227
205
|
help_text="If set, the configured value will remain even if the underlying Job source code changes",
|
|
@@ -244,90 +222,29 @@ class Job(PrimaryModel):
|
|
|
244
222
|
class Meta:
|
|
245
223
|
managed = True
|
|
246
224
|
ordering = ["grouping", "name"]
|
|
247
|
-
unique_together = ["
|
|
225
|
+
unique_together = ["module_name", "job_class_name"]
|
|
248
226
|
|
|
249
227
|
def __init__(self, *args, **kwargs):
|
|
250
228
|
super().__init__(*args, **kwargs)
|
|
251
|
-
self._job_class = None
|
|
252
229
|
self._latest_result = None
|
|
253
230
|
|
|
254
231
|
def __str__(self):
|
|
255
232
|
return self.name
|
|
256
233
|
|
|
257
|
-
|
|
258
|
-
"""
|
|
259
|
-
Check for duplicate (source, module_name, job_class_name) in the case where git_repository is None.
|
|
260
|
-
|
|
261
|
-
This is needed because NULL != NULL and so the unique_together constraint will not flag this case.
|
|
262
|
-
"""
|
|
263
|
-
if self.git_repository is None:
|
|
264
|
-
if Job.objects.exclude(pk=self.pk).filter(
|
|
265
|
-
source=self.source, module_name=self.module_name, job_class_name=self.job_class_name
|
|
266
|
-
):
|
|
267
|
-
raise ValidationError(
|
|
268
|
-
{"job_class_name": "A Job already exists with this source, module_name, and job_class_name"}
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
super().validate_unique(exclude=exclude)
|
|
272
|
-
|
|
273
|
-
@property
|
|
234
|
+
@cached_property
|
|
274
235
|
def job_class(self):
|
|
275
236
|
"""Get the Job class (source code) associated with this Job model."""
|
|
276
237
|
if not self.installed:
|
|
277
238
|
return None
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
self._job_class = job_info.job_class
|
|
284
|
-
break
|
|
285
|
-
else:
|
|
286
|
-
logger.warning("Module %s job class %s not found!", self.module_name, self.job_class_name)
|
|
287
|
-
elif self.source == JobSourceChoices.SOURCE_GIT:
|
|
288
|
-
from nautobot.extras.datasources.git import ensure_git_repository
|
|
289
|
-
|
|
290
|
-
if self.git_repository is None:
|
|
291
|
-
logger.warning("Job %s %s has no associated Git repository", self.module_name, self.job_class_name)
|
|
292
|
-
return None
|
|
293
|
-
try:
|
|
294
|
-
# In the case where we have multiple Nautobot instances, or multiple worker instances,
|
|
295
|
-
# they are not required to share a common filesystem; therefore, we may need to refresh our local
|
|
296
|
-
# clone of the Git repository to ensure that it is in sync with the latest repository clone
|
|
297
|
-
# from any instance.
|
|
298
|
-
ensure_git_repository(
|
|
299
|
-
self.git_repository,
|
|
300
|
-
head=self.git_repository.current_head,
|
|
301
|
-
logger=logger,
|
|
302
|
-
)
|
|
303
|
-
path = os.path.join(self.git_repository.filesystem_path, "jobs")
|
|
304
|
-
for job_info in jobs_in_directory(path, module_name=self.module_name):
|
|
305
|
-
if job_info.job_class_name == self.job_class_name:
|
|
306
|
-
self._job_class = job_info.job_class
|
|
307
|
-
break
|
|
308
|
-
else:
|
|
309
|
-
logger.warning(
|
|
310
|
-
"Module %s job class %s not found in repository %s",
|
|
311
|
-
self.module_name,
|
|
312
|
-
self.job_class_name,
|
|
313
|
-
self.git_repository,
|
|
314
|
-
)
|
|
315
|
-
except ObjectDoesNotExist:
|
|
316
|
-
return None
|
|
317
|
-
except Exception as exc:
|
|
318
|
-
logger.error(f"Error during local clone/refresh of Git repository {self.git_repository}: {exc}")
|
|
319
|
-
return None
|
|
320
|
-
elif self.source == JobSourceChoices.SOURCE_PLUGIN:
|
|
321
|
-
# pkgutil.resolve_name is only available in Python 3.9 and later
|
|
322
|
-
self._job_class = import_object(f"{self.module_name}.{self.job_class_name}")
|
|
323
|
-
|
|
324
|
-
return self._job_class
|
|
239
|
+
try:
|
|
240
|
+
return self.job_task.__class__
|
|
241
|
+
except Exception as exc:
|
|
242
|
+
logger.error(str(exc))
|
|
243
|
+
return None
|
|
325
244
|
|
|
326
245
|
@property
|
|
327
246
|
def class_path(self):
|
|
328
|
-
|
|
329
|
-
return f"{self.source}.{self.git_repository.slug}/{self.module_name}/{self.job_class_name}"
|
|
330
|
-
return f"{self.source}/{self.module_name}/{self.job_class_name}"
|
|
247
|
+
return f"{self.module_name}.{self.job_class_name}"
|
|
331
248
|
|
|
332
249
|
@property
|
|
333
250
|
def latest_result(self):
|
|
@@ -341,12 +258,25 @@ class Job(PrimaryModel):
|
|
|
341
258
|
|
|
342
259
|
@property
|
|
343
260
|
def runnable(self):
|
|
344
|
-
return (
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
261
|
+
return self.enabled and self.installed and not (self.has_sensitive_variables and self.approval_required)
|
|
262
|
+
|
|
263
|
+
@cached_property
|
|
264
|
+
def git_repository(self):
|
|
265
|
+
"""GitRepository record, if any, that owns this Job."""
|
|
266
|
+
try:
|
|
267
|
+
return GitRepository.objects.get(slug=self.module_name.split(".")[0])
|
|
268
|
+
except GitRepository.DoesNotExist:
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def job_task(self):
|
|
273
|
+
"""Get the registered Celery task, refreshing it if necessary."""
|
|
274
|
+
if self.git_repository is not None:
|
|
275
|
+
# If this Job comes from a Git repository, make sure we have the correct version of said code.
|
|
276
|
+
refresh_git_repository(
|
|
277
|
+
state=None, repository_pk=self.git_repository.pk, head=self.git_repository.current_head
|
|
278
|
+
)
|
|
279
|
+
return app.tasks[f"{self.module_name}.{self.job_class_name}"]
|
|
350
280
|
|
|
351
281
|
def clean(self):
|
|
352
282
|
"""For any non-overridden fields, make sure they get reset to the actual underlying class value if known."""
|
|
@@ -355,12 +285,7 @@ class Job(PrimaryModel):
|
|
|
355
285
|
if not getattr(self, f"{field_name}_override", False):
|
|
356
286
|
setattr(self, field_name, getattr(self.job_class, field_name))
|
|
357
287
|
|
|
358
|
-
if self.git_repository is not None and self.source != JobSourceChoices.SOURCE_GIT:
|
|
359
|
-
raise ValidationError('A Git repository may only be specified when the source is "git"')
|
|
360
|
-
|
|
361
288
|
# Protect against invalid input when auto-creating Job records
|
|
362
|
-
if len(self.source) > JOB_MAX_SOURCE_LENGTH:
|
|
363
|
-
raise ValidationError(f"Source may not exceed {JOB_MAX_SOURCE_LENGTH} characters in length")
|
|
364
289
|
if len(self.module_name) > JOB_MAX_NAME_LENGTH:
|
|
365
290
|
raise ValidationError(f"Module name may not exceed {JOB_MAX_NAME_LENGTH} characters in length")
|
|
366
291
|
if len(self.job_class_name) > JOB_MAX_NAME_LENGTH:
|
|
@@ -369,17 +294,12 @@ class Job(PrimaryModel):
|
|
|
369
294
|
raise ValidationError(f"Grouping may not exceed {JOB_MAX_GROUPING_LENGTH} characters in length")
|
|
370
295
|
if len(self.name) > JOB_MAX_NAME_LENGTH:
|
|
371
296
|
raise ValidationError(f"Name may not exceed {JOB_MAX_NAME_LENGTH} characters in length")
|
|
372
|
-
if len(self.slug) > JOB_MAX_NAME_LENGTH:
|
|
373
|
-
raise ValidationError(f"Slug may not exceed {JOB_MAX_NAME_LENGTH} characters in length")
|
|
374
297
|
|
|
375
298
|
if self.has_sensitive_variables is True and self.approval_required is True:
|
|
376
299
|
raise ValidationError(
|
|
377
300
|
{"approval_required": "A job that may have sensitive variables cannot be marked as requiring approval"}
|
|
378
301
|
)
|
|
379
302
|
|
|
380
|
-
def get_absolute_url(self):
|
|
381
|
-
return reverse("extras:job_detail", kwargs={"slug": self.slug})
|
|
382
|
-
|
|
383
303
|
|
|
384
304
|
@extras_features("graphql")
|
|
385
305
|
class JobHook(OrganizationalModel):
|
|
@@ -406,7 +326,6 @@ class JobHook(OrganizationalModel):
|
|
|
406
326
|
limit_choices_to={"is_job_hook_receiver": True},
|
|
407
327
|
)
|
|
408
328
|
name = models.CharField(max_length=100, unique=True)
|
|
409
|
-
slug = AutoSlugField(populate_from="name")
|
|
410
329
|
type_create = models.BooleanField(default=False, help_text="Call this job hook when a matching object is created.")
|
|
411
330
|
type_delete = models.BooleanField(default=False, help_text="Call this job hook when a matching object is deleted.")
|
|
412
331
|
type_update = models.BooleanField(default=False, help_text="Call this job hook when a matching object is updated.")
|
|
@@ -424,9 +343,6 @@ class JobHook(OrganizationalModel):
|
|
|
424
343
|
if not self.type_create and not self.type_delete and not self.type_update:
|
|
425
344
|
raise ValidationError("You must select at least one type: create, update, and/or delete.")
|
|
426
345
|
|
|
427
|
-
def get_absolute_url(self):
|
|
428
|
-
return reverse("extras:jobhook", kwargs={"slug": self.slug})
|
|
429
|
-
|
|
430
346
|
@classmethod
|
|
431
347
|
def check_for_conflicts(
|
|
432
348
|
cls, instance=None, content_types=None, job=None, type_create=None, type_update=None, type_delete=None
|
|
@@ -485,7 +401,7 @@ class JobLogEntry(BaseModel):
|
|
|
485
401
|
|
|
486
402
|
job_result = models.ForeignKey(to="extras.JobResult", on_delete=models.CASCADE, related_name="job_log_entries")
|
|
487
403
|
log_level = models.CharField(
|
|
488
|
-
max_length=32, choices=LogLevelChoices, default=LogLevelChoices.
|
|
404
|
+
max_length=32, choices=LogLevelChoices, default=LogLevelChoices.LOG_INFO, db_index=True
|
|
489
405
|
)
|
|
490
406
|
grouping = models.CharField(max_length=JOB_LOG_MAX_GROUPING_LENGTH, default="main")
|
|
491
407
|
message = models.TextField(blank=True)
|
|
@@ -497,8 +413,6 @@ class JobLogEntry(BaseModel):
|
|
|
497
413
|
log_object = models.CharField(max_length=JOB_LOG_MAX_LOG_OBJECT_LENGTH, blank=True, default="")
|
|
498
414
|
absolute_url = models.CharField(max_length=JOB_LOG_MAX_ABSOLUTE_URL_LENGTH, blank=True, default="")
|
|
499
415
|
|
|
500
|
-
csv_headers = ["created", "grouping", "log_level", "log_object", "message"]
|
|
501
|
-
|
|
502
416
|
def __str__(self):
|
|
503
417
|
return self.message
|
|
504
418
|
|
|
@@ -507,10 +421,6 @@ class JobLogEntry(BaseModel):
|
|
|
507
421
|
get_latest_by = "created"
|
|
508
422
|
verbose_name_plural = "job log entries"
|
|
509
423
|
|
|
510
|
-
def to_csv(self):
|
|
511
|
-
"""Indicates model fields to return as csv."""
|
|
512
|
-
return (str(self.created), self.grouping, self.log_level, self.log_object, self.message)
|
|
513
|
-
|
|
514
424
|
|
|
515
425
|
#
|
|
516
426
|
# Job results
|
|
@@ -539,18 +449,8 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
539
449
|
db_index=True,
|
|
540
450
|
help_text="Registered name of the Celery task for this job. Internal use only.",
|
|
541
451
|
)
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
related_name="job_results",
|
|
545
|
-
verbose_name="Object types",
|
|
546
|
-
limit_choices_to=FeatureQuery("job_results"),
|
|
547
|
-
help_text="The object type to which this job result applies",
|
|
548
|
-
on_delete=models.CASCADE,
|
|
549
|
-
null=True,
|
|
550
|
-
blank=True,
|
|
551
|
-
)
|
|
552
|
-
date_created = models.DateTimeField(auto_now_add=True)
|
|
553
|
-
date_done = models.DateTimeField(null=True, blank=True)
|
|
452
|
+
date_created = models.DateTimeField(auto_now_add=True, db_index=True)
|
|
453
|
+
date_done = models.DateTimeField(null=True, blank=True, db_index=True)
|
|
554
454
|
user = models.ForeignKey(
|
|
555
455
|
to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="+", blank=True, null=True
|
|
556
456
|
)
|
|
@@ -564,23 +464,16 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
564
464
|
data = models.JSONField(encoder=DjangoJSONEncoder, null=True, blank=True)
|
|
565
465
|
"""
|
|
566
466
|
Although "data" is technically an unstructured field, we have a standard structure that we try to adhere to.
|
|
567
|
-
|
|
568
467
|
This structure is created loosely as a superset of the formats used by Scripts and Reports in NetBox 2.10.
|
|
569
|
-
|
|
570
468
|
Log Messages now go to their own object, the JobLogEntry.
|
|
571
|
-
|
|
572
469
|
data = {
|
|
573
470
|
"output": <optional string, such as captured stdout/stderr>,
|
|
574
471
|
}
|
|
575
472
|
"""
|
|
576
|
-
periodic_task_name = models.CharField(
|
|
577
|
-
null=True,
|
|
578
|
-
max_length=255,
|
|
579
|
-
help_text="Registered name of the Celery periodic task for this job. Internal use only.",
|
|
580
|
-
)
|
|
581
473
|
worker = models.CharField(max_length=100, default=None, null=True)
|
|
582
|
-
task_args = models.JSONField(blank=True,
|
|
583
|
-
task_kwargs = models.JSONField(blank=True,
|
|
474
|
+
task_args = models.JSONField(blank=True, default=list, encoder=NautobotKombuJSONEncoder)
|
|
475
|
+
task_kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
|
|
476
|
+
celery_kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
|
|
584
477
|
# TODO(jathan): This field is currently unused for Jobs, but we should coerce it to a JSONField
|
|
585
478
|
# and set a contract that anything returned from a Job task MUST be JSON. In DCR core it is
|
|
586
479
|
# expected to be encoded/decoded using `content_type` and `content_encoding` which we have
|
|
@@ -596,8 +489,6 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
596
489
|
meta = models.JSONField(null=True, default=None, editable=False)
|
|
597
490
|
scheduled_job = models.ForeignKey(to="extras.ScheduledJob", on_delete=models.SET_NULL, null=True, blank=True)
|
|
598
491
|
|
|
599
|
-
task_id = models.UUIDField(unique=True)
|
|
600
|
-
|
|
601
492
|
objects = JobResultManager()
|
|
602
493
|
|
|
603
494
|
def __init__(self, *args, **kwargs):
|
|
@@ -607,14 +498,34 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
607
498
|
class Meta:
|
|
608
499
|
ordering = ["-date_created"]
|
|
609
500
|
get_latest_by = "date_created"
|
|
501
|
+
indexes = [
|
|
502
|
+
models.Index(
|
|
503
|
+
name="extras_jobresult_rcreated_idx",
|
|
504
|
+
fields=["-date_created"],
|
|
505
|
+
),
|
|
506
|
+
models.Index(
|
|
507
|
+
name="extras_jr_rdone_idx",
|
|
508
|
+
fields=["-date_done"],
|
|
509
|
+
),
|
|
510
|
+
models.Index(
|
|
511
|
+
name="extras_jr_statrcreate_idx",
|
|
512
|
+
fields=["status", "-date_created"],
|
|
513
|
+
),
|
|
514
|
+
models.Index(
|
|
515
|
+
name="extras_jr_statrdone_idx",
|
|
516
|
+
fields=["status", "-date_done"],
|
|
517
|
+
),
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
natural_key_field_names = ["id"]
|
|
610
521
|
|
|
611
522
|
def __str__(self):
|
|
612
|
-
return
|
|
523
|
+
return f"{self.name} started at {self.date_created} ({self.status})"
|
|
613
524
|
|
|
614
525
|
def as_dict(self):
|
|
615
526
|
"""This is required by the django-celery-results DB backend."""
|
|
616
527
|
return {
|
|
617
|
-
"
|
|
528
|
+
"id": self.id,
|
|
618
529
|
"task_name": self.task_name,
|
|
619
530
|
"task_args": self.task_args,
|
|
620
531
|
"task_kwargs": self.task_kwargs,
|
|
@@ -636,92 +547,8 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
636
547
|
|
|
637
548
|
return f"{int(minutes)} minutes, {seconds:.2f} seconds"
|
|
638
549
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
"""Get the related object, if any, identified by the `obj_type`, `name`, and/or `task_id` fields.
|
|
642
|
-
|
|
643
|
-
If `obj_type` is extras.Job, then the `name` is used to look up an extras.jobs.Job subclass based on the
|
|
644
|
-
`class_path` of the Job subclass.
|
|
645
|
-
Note that this is **not** the extras.models.Job model class nor an instance thereof.
|
|
646
|
-
|
|
647
|
-
Else, if the the model class referenced by `obj_type` has a `name` field, our `name` field will be used
|
|
648
|
-
to look up a corresponding model instance. This is used, for example, to look up a related `GitRepository`;
|
|
649
|
-
more generally it can be used by any model that 1) has a unique `name` field and 2) needs to have a many-to-one
|
|
650
|
-
relationship between JobResults and model instances.
|
|
651
|
-
|
|
652
|
-
Else, the `obj_type` and `task_id` will be used together as a quasi-GenericForeignKey to look up a model
|
|
653
|
-
instance whose PK corresponds to the `task_id`. This behavior is currently unused in the Nautobot core,
|
|
654
|
-
but may be of use to plugin developers wishing to create JobResults that have a one-to-one relationship
|
|
655
|
-
to plugin model instances.
|
|
656
|
-
|
|
657
|
-
This method is potentially rather slow as get_job() may need to actually load the Job class from disk;
|
|
658
|
-
consider carefully whether you actually need to use it.
|
|
659
|
-
"""
|
|
660
|
-
from nautobot.extras.jobs import get_job # needed here to avoid a circular import issue
|
|
661
|
-
|
|
662
|
-
if self.obj_type == get_job_content_type():
|
|
663
|
-
# Related object is an extras.Job subclass, our `name` matches its `class_path`
|
|
664
|
-
return get_job(self.name)
|
|
665
|
-
|
|
666
|
-
if self.obj_type:
|
|
667
|
-
model_class = self.obj_type.model_class()
|
|
668
|
-
else:
|
|
669
|
-
model_class = None
|
|
670
|
-
|
|
671
|
-
if model_class is not None:
|
|
672
|
-
if hasattr(model_class, "name"):
|
|
673
|
-
# See if we have a many-to-one relationship from JobResult to model_class record, based on `name`
|
|
674
|
-
try:
|
|
675
|
-
return model_class.objects.get(name=self.name)
|
|
676
|
-
except model_class.DoesNotExist:
|
|
677
|
-
pass
|
|
678
|
-
|
|
679
|
-
# See if we have a one-to-one relationship from JobResult to model_class record based on `task_id`
|
|
680
|
-
try:
|
|
681
|
-
return model_class.objects.get(id=self.task_id)
|
|
682
|
-
except model_class.DoesNotExist:
|
|
683
|
-
pass
|
|
684
|
-
|
|
685
|
-
return None
|
|
686
|
-
|
|
687
|
-
@property
|
|
688
|
-
def related_name(self):
|
|
689
|
-
"""
|
|
690
|
-
Similar to self.name, but if there's an appropriate `related_object`, use its name instead.
|
|
691
|
-
|
|
692
|
-
Since this calls related_object, the same potential performance concerns exist. Use with caution.
|
|
693
|
-
"""
|
|
694
|
-
related_object = self.related_object
|
|
695
|
-
if not related_object:
|
|
696
|
-
return self.name
|
|
697
|
-
if hasattr(related_object, "name"):
|
|
698
|
-
return related_object.name
|
|
699
|
-
return str(related_object)
|
|
700
|
-
|
|
701
|
-
@property
|
|
702
|
-
def linked_record(self):
|
|
703
|
-
"""
|
|
704
|
-
A newer alternative to self.related_object that looks up an extras.models.Job instead of an extras.jobs.Job.
|
|
705
|
-
"""
|
|
706
|
-
if self.job_model is not None:
|
|
707
|
-
return self.job_model
|
|
708
|
-
model_class = self.obj_type.model_class()
|
|
709
|
-
if model_class is not None:
|
|
710
|
-
if hasattr(model_class, "name"):
|
|
711
|
-
try:
|
|
712
|
-
return model_class.objects.get(name=self.name)
|
|
713
|
-
except model_class.DoesNotExist:
|
|
714
|
-
pass
|
|
715
|
-
if hasattr(model_class, "class_path"):
|
|
716
|
-
try:
|
|
717
|
-
return model_class.objects.get(class_path=self.name)
|
|
718
|
-
except model_class.DoesNotExist:
|
|
719
|
-
pass
|
|
720
|
-
return None
|
|
721
|
-
|
|
722
|
-
def get_absolute_url(self):
|
|
723
|
-
return reverse("extras:jobresult", kwargs={"pk": self.pk})
|
|
724
|
-
|
|
550
|
+
# FIXME(jathan): This needs to go away. Need to think about that the impact
|
|
551
|
+
# will be in the JOB_RESULT_METRIC and how to compensate for it.
|
|
725
552
|
def set_status(self, status):
|
|
726
553
|
"""
|
|
727
554
|
Helper method to change the status of the job result. If the target status is terminal, the completion
|
|
@@ -739,69 +566,118 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
739
566
|
)
|
|
740
567
|
|
|
741
568
|
@classmethod
|
|
742
|
-
def
|
|
569
|
+
def execute_job(cls, job_model, user, *job_args, celery_kwargs=None, profile=False, **job_kwargs):
|
|
743
570
|
"""
|
|
744
|
-
Create a JobResult instance and
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
571
|
+
Create a JobResult instance and run a job in the current process, blocking until the job finishes.
|
|
572
|
+
|
|
573
|
+
Running tasks synchronously in celery is *NOT* supported and if possible `enqueue_job` with synchronous=False
|
|
574
|
+
should be used instead.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
job_model (Job): The Job to be enqueued for execution
|
|
578
|
+
user (User): User object to link to the JobResult instance
|
|
579
|
+
celery_kwargs (dict, optional): Dictionary of kwargs to pass as **kwargs to Celery when job is run
|
|
580
|
+
profile (bool, optional): Whether to run cProfile on the job execution
|
|
581
|
+
*job_args: positional args passed to the job task
|
|
582
|
+
**job_kwargs: keyword args passed to the job task
|
|
583
|
+
|
|
584
|
+
Returns:
|
|
585
|
+
JobResult instance
|
|
586
|
+
"""
|
|
587
|
+
return cls.enqueue_job(
|
|
588
|
+
job_model, user, *job_args, celery_kwargs=celery_kwargs, profile=profile, synchronous=True, **job_kwargs
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
@classmethod
|
|
592
|
+
def enqueue_job(
|
|
593
|
+
cls,
|
|
594
|
+
job_model,
|
|
595
|
+
user,
|
|
596
|
+
*job_args,
|
|
597
|
+
celery_kwargs=None,
|
|
598
|
+
profile=False,
|
|
599
|
+
schedule=None,
|
|
600
|
+
task_queue=None,
|
|
601
|
+
synchronous=False,
|
|
602
|
+
**job_kwargs,
|
|
603
|
+
):
|
|
604
|
+
"""Create a JobResult instance and enqueue a job to be executed asynchronously by a Celery worker.
|
|
605
|
+
|
|
606
|
+
Args:
|
|
607
|
+
job_model (Job): The Job to be enqueued for execution.
|
|
608
|
+
user (User): User object to link to the JobResult instance.
|
|
609
|
+
celery_kwargs (dict, optional): Dictionary of kwargs to pass as **kwargs to `apply_async()`/`apply()` when job is run.
|
|
610
|
+
profile (bool, optional): If True, dump cProfile stats on the job execution.
|
|
611
|
+
schedule (ScheduledJob, optional): ScheduledJob instance to link to the JobResult. Cannot be used with synchronous=True.
|
|
612
|
+
task_queue (str, optional): The celery queue to send the job to. If not set, use the default celery queue.
|
|
613
|
+
synchronous (bool, optional): If True, run the job in the current process, blocking until the job completes.
|
|
614
|
+
*job_args: positional args passed to the job task
|
|
615
|
+
**job_kwargs: keyword args passed to the job task
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
JobResult instance
|
|
755
619
|
"""
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
620
|
+
if schedule is not None and synchronous:
|
|
621
|
+
raise ValueError("Scheduled jobs cannot be run synchronously")
|
|
622
|
+
|
|
759
623
|
job_result = cls.objects.create(
|
|
760
|
-
name=name,
|
|
761
|
-
|
|
762
|
-
user=user,
|
|
763
|
-
task_id=uuid.uuid4(),
|
|
624
|
+
name=job_model.name,
|
|
625
|
+
job_model=job_model,
|
|
764
626
|
scheduled_job=schedule,
|
|
627
|
+
user=user,
|
|
765
628
|
)
|
|
766
629
|
|
|
767
|
-
|
|
630
|
+
if task_queue is None:
|
|
631
|
+
task_queue = settings.CELERY_TASK_DEFAULT_QUEUE
|
|
768
632
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
633
|
+
job_celery_kwargs = {
|
|
634
|
+
"nautobot_job_job_model_id": job_model.id,
|
|
635
|
+
"nautobot_job_profile": profile,
|
|
636
|
+
"nautobot_job_user_id": user.id,
|
|
637
|
+
"queue": task_queue,
|
|
638
|
+
}
|
|
772
639
|
|
|
773
|
-
if
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
640
|
+
if schedule is not None:
|
|
641
|
+
job_celery_kwargs["nautobot_job_schedule_id"] = schedule.id
|
|
642
|
+
if job_model.soft_time_limit > 0:
|
|
643
|
+
job_celery_kwargs["soft_time_limit"] = job_model.soft_time_limit
|
|
644
|
+
if job_model.time_limit > 0:
|
|
645
|
+
job_celery_kwargs["time_limit"] = job_model.time_limit
|
|
646
|
+
|
|
647
|
+
if celery_kwargs is not None:
|
|
648
|
+
job_celery_kwargs.update(celery_kwargs)
|
|
649
|
+
|
|
650
|
+
if synchronous:
|
|
651
|
+
# synchronous tasks are run before the JobResult is saved, so any fields required by
|
|
652
|
+
# the job must be added before calling `apply()`
|
|
653
|
+
job_result.celery_kwargs = job_celery_kwargs
|
|
654
|
+
job_result.save()
|
|
655
|
+
|
|
656
|
+
# setup synchronous task logging
|
|
657
|
+
redirect_logger = get_logger("celery.redirected")
|
|
658
|
+
add_nautobot_log_handler(redirect_logger)
|
|
659
|
+
setup_nautobot_job_logging(None, None, app.conf)
|
|
660
|
+
|
|
661
|
+
# redirect stdout/stderr to logger and run task
|
|
662
|
+
proxy = LoggingProxy(redirect_logger, app.conf.worker_redirect_stdouts_level)
|
|
663
|
+
with contextlib.redirect_stdout(proxy), contextlib.redirect_stderr(proxy):
|
|
664
|
+
eager_result = job_model.job_task.apply(
|
|
665
|
+
args=job_args, kwargs=job_kwargs, task_id=str(job_result.id), **job_celery_kwargs
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
# copy fields from eager result to job result
|
|
669
|
+
job_result.refresh_from_db()
|
|
670
|
+
for field in ["status", "result", "traceback", "worker"]:
|
|
671
|
+
setattr(job_result, field, getattr(eager_result, field, None))
|
|
672
|
+
job_result.date_done = timezone.now()
|
|
673
|
+
job_result.save()
|
|
674
|
+
else:
|
|
675
|
+
# Jobs queued inside of a transaction need to run after the transaction completes and the JobResult is saved to the database
|
|
676
|
+
transaction.on_commit(
|
|
677
|
+
lambda: job_model.job_task.apply_async(
|
|
678
|
+
args=job_args, kwargs=job_kwargs, task_id=str(job_result.id), **job_celery_kwargs
|
|
679
|
+
)
|
|
680
|
+
)
|
|
805
681
|
|
|
806
682
|
return job_result
|
|
807
683
|
|
|
@@ -809,7 +685,7 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
809
685
|
self,
|
|
810
686
|
message,
|
|
811
687
|
obj=None,
|
|
812
|
-
level_choice=LogLevelChoices.
|
|
688
|
+
level_choice=LogLevelChoices.LOG_INFO,
|
|
813
689
|
grouping="main",
|
|
814
690
|
logger=None, # pylint: disable=redefined-outer-name
|
|
815
691
|
):
|
|
@@ -823,7 +699,7 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
823
699
|
logger (logging.logger): Optional logger to also output the message to
|
|
824
700
|
"""
|
|
825
701
|
if level_choice not in LogLevelChoices.as_dict():
|
|
826
|
-
raise
|
|
702
|
+
raise ValueError(f"Unknown logging level: {level_choice}")
|
|
827
703
|
|
|
828
704
|
message = sanitize(str(message))
|
|
829
705
|
|
|
@@ -849,12 +725,7 @@ class JobResult(BaseModel, CustomFieldModel):
|
|
|
849
725
|
log.save(using=JOB_LOGS)
|
|
850
726
|
|
|
851
727
|
if logger:
|
|
852
|
-
|
|
853
|
-
log_level = logging.ERROR
|
|
854
|
-
elif level_choice == LogLevelChoices.LOG_WARNING:
|
|
855
|
-
log_level = logging.WARNING
|
|
856
|
-
else:
|
|
857
|
-
log_level = logging.INFO
|
|
728
|
+
log_level = getattr(logging, level_choice.upper(), logging.INFO)
|
|
858
729
|
logger.log(log_level, message)
|
|
859
730
|
|
|
860
731
|
|
|
@@ -909,9 +780,6 @@ class JobButton(BaseModel, ChangeLoggedModel, NotesMixin):
|
|
|
909
780
|
def __str__(self):
|
|
910
781
|
return self.name
|
|
911
782
|
|
|
912
|
-
def get_absolute_url(self):
|
|
913
|
-
return reverse("extras:jobbutton", kwargs={"pk": self.pk})
|
|
914
|
-
|
|
915
783
|
|
|
916
784
|
class ScheduledJobs(models.Model):
|
|
917
785
|
"""Helper table for tracking updates to scheduled tasks.
|
|
@@ -977,6 +845,7 @@ class ScheduledJob(BaseModel):
|
|
|
977
845
|
interval = models.CharField(choices=JobExecutionType, max_length=255)
|
|
978
846
|
args = models.JSONField(blank=True, default=list, encoder=NautobotKombuJSONEncoder)
|
|
979
847
|
kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
|
|
848
|
+
celery_kwargs = models.JSONField(blank=True, default=dict, encoder=NautobotKombuJSONEncoder)
|
|
980
849
|
queue = models.CharField(
|
|
981
850
|
max_length=200,
|
|
982
851
|
blank=True,
|
|
@@ -1062,13 +931,11 @@ class ScheduledJob(BaseModel):
|
|
|
1062
931
|
def __str__(self):
|
|
1063
932
|
return f"{self.name}: {self.interval}"
|
|
1064
933
|
|
|
1065
|
-
|
|
1066
|
-
|
|
934
|
+
# TODO: there's currently no natural key for ScheduledJob
|
|
935
|
+
natural_key_field_names = ["id"]
|
|
1067
936
|
|
|
1068
937
|
def save(self, *args, **kwargs):
|
|
1069
938
|
self.queue = self.queue or ""
|
|
1070
|
-
# pass pk to worker task in kwargs, celery doesn't provide the full object to the worker
|
|
1071
|
-
self.kwargs["scheduled_job_pk"] = self.pk
|
|
1072
939
|
# make sure non-valid crontab doesn't get saved
|
|
1073
940
|
if self.interval == JobExecutionType.TYPE_CUSTOM:
|
|
1074
941
|
try:
|