nautobot 2.0.0a3__py3-none-any.whl → 2.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nautobot/apps/api.py +6 -8
- nautobot/apps/forms.py +0 -2
- nautobot/apps/ui.py +0 -8
- nautobot/circuits/api/serializers.py +9 -117
- nautobot/circuits/api/urls.py +1 -1
- nautobot/circuits/api/views.py +0 -1
- nautobot/circuits/forms.py +0 -65
- nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
- nautobot/circuits/migrations/0016_tagsfield.py +34 -0
- nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
- nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
- nautobot/circuits/models.py +3 -87
- nautobot/circuits/navigation.py +14 -69
- nautobot/circuits/signals.py +0 -2
- nautobot/circuits/tables.py +39 -1
- nautobot/circuits/tests/integration/test_relationships.py +9 -9
- nautobot/circuits/tests/test_api.py +4 -8
- nautobot/circuits/tests/test_filters.py +10 -4
- nautobot/circuits/tests/test_models.py +5 -1
- nautobot/circuits/tests/test_views.py +27 -5
- nautobot/circuits/views.py +18 -10
- nautobot/core/api/__init__.py +8 -2
- nautobot/core/api/fields.py +15 -6
- nautobot/core/api/filter_backends.py +3 -2
- nautobot/core/api/metadata.py +237 -30
- nautobot/core/api/mixins.py +94 -0
- nautobot/core/api/pagination.py +4 -0
- nautobot/core/api/parsers.py +154 -0
- nautobot/core/api/renderers.py +153 -2
- nautobot/core/api/schema.py +46 -2
- nautobot/core/api/serializers.py +377 -35
- nautobot/core/api/urls.py +11 -3
- nautobot/core/api/utils.py +174 -2
- nautobot/core/api/versioning.py +32 -10
- nautobot/core/api/views.py +266 -72
- nautobot/core/apps/__init__.py +138 -220
- nautobot/core/celery/__init__.py +112 -41
- nautobot/core/celery/backends.py +19 -12
- nautobot/core/celery/control.py +46 -0
- nautobot/core/celery/encoders.py +53 -0
- nautobot/core/celery/log.py +38 -0
- nautobot/core/celery/schedulers.py +23 -4
- nautobot/core/celery/task.py +1 -16
- nautobot/core/checks.py +0 -27
- nautobot/core/choices.py +0 -113
- nautobot/core/{cli.py → cli/__init__.py} +1 -1
- nautobot/core/cli/__main__.py +3 -0
- nautobot/core/constants.py +0 -24
- nautobot/core/context_processors.py +12 -0
- nautobot/core/filters.py +2 -2
- nautobot/core/forms/__init__.py +0 -4
- nautobot/core/forms/fields.py +38 -65
- nautobot/core/forms/forms.py +4 -1
- nautobot/core/forms/utils.py +0 -52
- nautobot/core/graphql/schema.py +4 -27
- nautobot/core/jobs/__init__.py +75 -0
- nautobot/core/management/commands/build_ui.py +255 -0
- nautobot/core/management/commands/generate_test_data.py +3 -2
- nautobot/core/management/commands/post_upgrade.py +24 -24
- nautobot/core/models/__init__.py +26 -1
- nautobot/core/models/fields.py +24 -5
- nautobot/core/models/generics.py +2 -42
- nautobot/core/models/managers.py +5 -0
- nautobot/core/models/name_color_content_types.py +0 -14
- nautobot/core/models/tree_queries.py +14 -4
- nautobot/core/models/utils.py +5 -6
- nautobot/core/models/validators.py +17 -8
- nautobot/core/releases.py +8 -10
- nautobot/core/settings.py +80 -42
- nautobot/core/tables.py +5 -5
- nautobot/core/tasks.py +4 -7
- nautobot/core/templates/base.html +1 -49
- nautobot/core/templates/base_django.html +49 -0
- nautobot/core/templates/base_react.html +55 -0
- nautobot/core/templates/buttons/export.html +6 -4
- nautobot/core/templates/generic/object_bulk_create.html +10 -21
- nautobot/core/templates/generic/object_list.html +3 -1
- nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
- nautobot/core/templates/inc/footer.html +1 -0
- nautobot/core/templates/inc/javascript.html +0 -14
- nautobot/core/templates/inc/nav_menu.html +28 -33
- nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
- nautobot/core/templates/inc/relationships_table_rows.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +8 -20
- nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
- nautobot/core/templates/rest_framework/api.html +8 -0
- nautobot/core/templatetags/buttons.py +32 -28
- nautobot/core/testing/__init__.py +47 -44
- nautobot/core/testing/api.py +362 -47
- nautobot/core/testing/filters.py +1 -1
- nautobot/core/testing/migrations.py +2 -0
- nautobot/core/testing/mixins.py +22 -9
- nautobot/core/testing/schema.py +2 -1
- nautobot/core/testing/views.py +21 -46
- nautobot/core/tests/integration/test_filters.py +17 -8
- nautobot/core/tests/integration/test_navbar.py +11 -34
- nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
- nautobot/core/tests/nautobot_config.py +2 -3
- nautobot/core/tests/test_api.py +290 -21
- nautobot/core/tests/test_checks.py +0 -7
- nautobot/core/tests/test_filters.py +107 -59
- nautobot/core/tests/test_forms.py +26 -92
- nautobot/core/tests/test_graphql.py +110 -77
- nautobot/core/tests/test_logging.py +4 -0
- nautobot/core/tests/test_managers.py +3 -1
- nautobot/core/tests/test_models.py +2 -0
- nautobot/core/tests/test_paginator.py +3 -1
- nautobot/core/tests/test_releases.py +12 -12
- nautobot/core/tests/test_templatetags_helpers.py +4 -4
- nautobot/core/tests/test_utils.py +32 -68
- nautobot/core/tests/test_views.py +12 -15
- nautobot/core/utils/data.py +17 -0
- nautobot/core/utils/deprecation.py +9 -6
- nautobot/core/utils/filtering.py +8 -3
- nautobot/core/utils/git.py +12 -4
- nautobot/core/utils/lookup.py +3 -1
- nautobot/core/utils/requests.py +1 -104
- nautobot/core/views/__init__.py +1 -0
- nautobot/core/views/generic.py +75 -110
- nautobot/core/views/mixins.py +52 -61
- nautobot/core/views/renderers.py +6 -7
- nautobot/core/views/utils.py +80 -0
- nautobot/dcim/api/serializers.py +160 -667
- nautobot/dcim/api/urls.py +1 -1
- nautobot/dcim/api/views.py +7 -44
- nautobot/dcim/choices.py +2 -0
- nautobot/dcim/filters/__init__.py +21 -0
- nautobot/dcim/form_mixins.py +1 -27
- nautobot/dcim/forms.py +19 -765
- nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
- nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
- nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
- nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
- nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
- nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
- nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
- nautobot/dcim/migrations/0040_tagsfield.py +109 -0
- nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
- nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
- nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
- nautobot/dcim/models/cables.py +3 -33
- nautobot/dcim/models/device_component_templates.py +6 -0
- nautobot/dcim/models/device_components.py +12 -198
- nautobot/dcim/models/devices.py +30 -143
- nautobot/dcim/models/locations.py +3 -64
- nautobot/dcim/models/power.py +3 -50
- nautobot/dcim/models/racks.py +7 -84
- nautobot/dcim/navigation.py +141 -467
- nautobot/dcim/signals.py +0 -2
- nautobot/dcim/tables/locations.py +2 -2
- nautobot/dcim/tables/power.py +1 -2
- nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/devicetype.html +2 -2
- nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/location.html +16 -1
- nautobot/dcim/templates/dcim/locationtype.html +15 -0
- nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
- nautobot/dcim/templates/dcim/rackgroup.html +0 -12
- nautobot/dcim/tests/test_api.py +166 -81
- nautobot/dcim/tests/test_cablepaths.py +41 -35
- nautobot/dcim/tests/test_filters.py +67 -23
- nautobot/dcim/tests/test_forms.py +5 -205
- nautobot/dcim/tests/test_graphql.py +7 -2
- nautobot/dcim/tests/test_migrations.py +6 -11
- nautobot/dcim/tests/test_models.py +182 -110
- nautobot/dcim/tests/test_natural_ordering.py +11 -8
- nautobot/dcim/tests/test_signals.py +6 -3
- nautobot/dcim/tests/test_views.py +197 -175
- nautobot/dcim/urls.py +11 -16
- nautobot/dcim/views.py +7 -134
- nautobot/docs/additional-features/caching.md +6 -87
- nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
- nautobot/docs/additional-features/jobs.md +177 -195
- nautobot/docs/administration/nautobot-server.md +6 -21
- nautobot/docs/administration/replicating-nautobot.md +0 -10
- nautobot/docs/configuration/optional-settings.md +32 -41
- nautobot/docs/configuration/required-settings.md +11 -52
- nautobot/docs/development/application-registry.md +2 -13
- nautobot/docs/development/extending-models.md +15 -17
- nautobot/docs/development/generic-views.md +0 -2
- nautobot/docs/development/getting-started.md +55 -5
- nautobot/docs/development/navigation-menu.md +22 -93
- nautobot/docs/development/react-ui.md +105 -0
- nautobot/docs/development/role-internals.md +1 -3
- nautobot/docs/development/style-guide.md +6 -4
- nautobot/docs/index.md +3 -2
- nautobot/docs/installation/migrating-from-netbox.md +11 -42
- nautobot/docs/installation/nautobot.md +1 -1
- nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
- nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
- nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
- nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
- nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
- nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
- nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
- nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
- nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
- nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
- nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
- nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
- nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
- nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
- nautobot/docs/models/dcim/device.md +3 -0
- nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
- nautobot/docs/models/extras/computedfield.md +4 -4
- nautobot/docs/models/extras/gitrepository.md +3 -0
- nautobot/docs/models/extras/job.md +1 -0
- nautobot/docs/models/extras/jobbutton.md +18 -13
- nautobot/docs/models/extras/jobhook.md +7 -4
- nautobot/docs/models/extras/jobresult.md +6 -2
- nautobot/docs/models/extras/relationship.md +2 -2
- nautobot/docs/models/extras/status.md +6 -19
- nautobot/docs/models/ipam/ipaddress.md +3 -0
- nautobot/docs/models/virtualization/virtualmachine.md +3 -0
- nautobot/docs/plugins/development.md +83 -21
- nautobot/docs/release-notes/version-1.5.md +53 -0
- nautobot/docs/release-notes/version-2.0.md +180 -0
- nautobot/docs/requirements.txt +1 -0
- nautobot/docs/rest-api/overview.md +384 -215
- nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
- nautobot/extras/admin.py +3 -5
- nautobot/extras/api/customfields.py +15 -39
- nautobot/extras/api/fields.py +0 -11
- nautobot/extras/api/mixins.py +45 -0
- nautobot/extras/api/relationships.py +63 -158
- nautobot/extras/api/serializers.py +165 -700
- nautobot/extras/api/urls.py +1 -1
- nautobot/extras/api/views.py +294 -280
- nautobot/extras/apps.py +4 -7
- nautobot/extras/choices.py +11 -9
- nautobot/extras/constants.py +9 -3
- nautobot/extras/datasources/__init__.py +2 -0
- nautobot/extras/datasources/git.py +135 -186
- nautobot/extras/datasources/registry.py +25 -35
- nautobot/extras/filters/__init__.py +20 -19
- nautobot/extras/filters/mixins.py +4 -4
- nautobot/extras/forms/forms.py +63 -127
- nautobot/extras/forms/mixins.py +23 -51
- nautobot/extras/health_checks.py +0 -33
- nautobot/extras/jobs.py +387 -565
- nautobot/extras/management/commands/runjob.py +24 -62
- nautobot/extras/managers.py +30 -7
- nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
- nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
- nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
- nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
- nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
- nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
- nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
- nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
- nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
- nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
- nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
- nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
- nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
- nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
- nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
- nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
- nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
- nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
- nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
- nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
- nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
- nautobot/extras/migrations/0079_tagsfield.py +28 -0
- nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
- nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
- nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
- nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
- nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
- nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
- nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
- nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
- nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
- nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
- nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
- nautobot/extras/models/__init__.py +2 -3
- nautobot/extras/models/change_logging.py +0 -36
- nautobot/extras/models/customfields.py +39 -33
- nautobot/extras/models/datasources.py +48 -50
- nautobot/extras/models/groups.py +5 -6
- nautobot/extras/models/jobs.py +189 -321
- nautobot/extras/models/mixins.py +0 -71
- nautobot/extras/models/models.py +0 -19
- nautobot/extras/models/relationships.py +19 -13
- nautobot/extras/models/roles.py +0 -34
- nautobot/extras/models/secrets.py +2 -26
- nautobot/extras/models/statuses.py +6 -5
- nautobot/extras/models/tags.py +2 -17
- nautobot/extras/navigation.py +89 -307
- nautobot/extras/plugins/__init__.py +3 -120
- nautobot/extras/plugins/utils.py +0 -3
- nautobot/extras/plugins/validators.py +5 -4
- nautobot/extras/plugins/views.py +16 -3
- nautobot/extras/querysets.py +1 -7
- nautobot/extras/registry.py +3 -0
- nautobot/extras/signals.py +26 -60
- nautobot/extras/tables.py +34 -40
- nautobot/extras/tasks.py +0 -12
- nautobot/extras/templates/extras/configcontext.html +1 -1
- nautobot/extras/templates/extras/configcontextschema.html +16 -1
- nautobot/extras/templates/extras/customfield.html +0 -13
- nautobot/extras/templates/extras/gitrepository.html +3 -3
- nautobot/extras/templates/extras/inc/jobresult.html +10 -0
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
- nautobot/extras/templates/extras/job.html +35 -25
- nautobot/extras/templates/extras/job_approval_request.html +15 -30
- nautobot/extras/templates/extras/job_detail.html +13 -31
- nautobot/extras/templates/extras/job_edit.html +15 -17
- nautobot/extras/templates/extras/jobresult.html +24 -6
- nautobot/extras/templates/extras/scheduledjob.html +2 -2
- nautobot/extras/templates/extras/secret.html +28 -0
- nautobot/extras/templatetags/job_buttons.py +1 -0
- nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
- nautobot/extras/test_jobs/atomic_transaction.py +53 -0
- nautobot/extras/test_jobs/dry_run.py +29 -0
- nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
- nautobot/extras/test_jobs/duplicate_name2.py +9 -0
- nautobot/extras/test_jobs/fail.py +23 -0
- nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
- nautobot/extras/test_jobs/file_upload_pass.py +25 -0
- nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
- nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
- nautobot/extras/test_jobs/job_button_receiver.py +28 -0
- nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
- nautobot/extras/test_jobs/job_variables.py +88 -0
- nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
- nautobot/extras/test_jobs/log_redaction.py +20 -0
- nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
- nautobot/extras/test_jobs/modify_db.py +25 -0
- nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
- nautobot/extras/test_jobs/object_var_optional.py +21 -0
- nautobot/extras/test_jobs/object_var_required.py +21 -0
- nautobot/extras/test_jobs/object_vars.py +26 -0
- nautobot/extras/test_jobs/pass.py +25 -0
- nautobot/extras/test_jobs/profiling.py +32 -0
- nautobot/extras/test_jobs/read_only_job.py +15 -0
- nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
- nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
- nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
- nautobot/extras/tests/integration/test_computedfields.py +1 -1
- nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
- nautobot/extras/tests/integration/test_customfields.py +4 -2
- nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/integration/test_jobs.py +25 -27
- nautobot/extras/tests/integration/test_notes.py +8 -4
- nautobot/extras/tests/integration/test_relationships.py +2 -2
- nautobot/extras/tests/test_api.py +649 -642
- nautobot/extras/tests/test_changelog.py +3 -3
- nautobot/extras/tests/test_context_managers.py +5 -3
- nautobot/extras/tests/test_customfields.py +92 -50
- nautobot/extras/tests/test_datasources.py +189 -112
- nautobot/extras/tests/test_dynamicgroups.py +7 -8
- nautobot/extras/tests/test_filters.py +137 -89
- nautobot/extras/tests/test_forms.py +73 -75
- nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
- nautobot/extras/tests/test_jobs.py +262 -263
- nautobot/extras/tests/test_migrations.py +4 -3
- nautobot/extras/tests/test_models.py +116 -161
- nautobot/extras/tests/test_plugins.py +38 -60
- nautobot/extras/tests/test_relationships.py +167 -120
- nautobot/extras/tests/test_tags.py +6 -11
- nautobot/extras/tests/test_utils.py +31 -1
- nautobot/extras/tests/test_views.py +201 -145
- nautobot/extras/tests/test_webhooks.py +6 -2
- nautobot/extras/urls.py +42 -42
- nautobot/extras/utils.py +137 -163
- nautobot/extras/views.py +78 -152
- nautobot/ipam/api/fields.py +17 -0
- nautobot/ipam/api/serializers.py +58 -164
- nautobot/ipam/api/urls.py +1 -1
- nautobot/ipam/api/views.py +3 -2
- nautobot/ipam/apps.py +1 -2
- nautobot/ipam/filters.py +1 -10
- nautobot/ipam/forms.py +4 -177
- nautobot/ipam/lookups.py +1 -0
- nautobot/ipam/management/commands/__init__.py +0 -0
- nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
- nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
- nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
- nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
- nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
- nautobot/ipam/migrations/0028_tagsfield.py +44 -0
- nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
- nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
- nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
- nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
- nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
- nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
- nautobot/ipam/models.py +100 -236
- nautobot/ipam/navigation.py +36 -181
- nautobot/ipam/querysets.py +20 -25
- nautobot/ipam/signals.py +49 -6
- nautobot/ipam/tables.py +10 -3
- nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
- nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
- nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
- nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
- nautobot/ipam/templates/ipam/prefix.html +1 -1
- nautobot/ipam/templates/ipam/vlangroup.html +0 -13
- nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
- nautobot/ipam/tests/integration/test_prefixes.py +3 -26
- nautobot/ipam/tests/test_api.py +22 -19
- nautobot/ipam/tests/test_filters.py +59 -23
- nautobot/ipam/tests/test_migrations.py +6 -10
- nautobot/ipam/tests/test_models.py +323 -198
- nautobot/ipam/tests/test_ordering.py +2 -2
- nautobot/ipam/tests/test_querysets.py +44 -24
- nautobot/ipam/tests/test_views.py +73 -26
- nautobot/ipam/urls.py +16 -0
- nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
- nautobot/ipam/utils/migrations.py +713 -0
- nautobot/ipam/views.py +137 -20
- nautobot/project-static/docs/404.html +1178 -10
- nautobot/project-static/docs/additional-features/caching.html +1224 -159
- nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
- nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
- nautobot/project-static/docs/additional-features/graphql.html +1179 -11
- nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
- nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
- nautobot/project-static/docs/additional-features/jobs.html +1514 -328
- nautobot/project-static/docs/additional-features/napalm.html +1180 -12
- nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
- nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
- nautobot/project-static/docs/administration/celery-queues.html +1178 -10
- nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
- nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
- nautobot/project-static/docs/administration/permissions.html +1178 -10
- nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
- nautobot/project-static/docs/apps/index.html +1178 -10
- nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
- nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
- nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
- nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
- nautobot/project-static/docs/configuration/index.html +1178 -10
- nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
- nautobot/project-static/docs/configuration/required-settings.html +1312 -211
- nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
- nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
- nautobot/project-static/docs/core-functionality/devices.html +1182 -10
- nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
- nautobot/project-static/docs/core-functionality/power.html +1178 -10
- nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
- nautobot/project-static/docs/core-functionality/services.html +1178 -10
- nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
- nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
- nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
- nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
- nautobot/project-static/docs/development/application-registry.html +1190 -42
- nautobot/project-static/docs/development/best-practices.html +1178 -10
- nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
- nautobot/project-static/docs/development/extending-models.html +1238 -83
- nautobot/project-static/docs/development/generic-views.html +1180 -14
- nautobot/project-static/docs/development/getting-started.html +1365 -90
- nautobot/project-static/docs/development/homepage.html +1178 -10
- nautobot/project-static/docs/development/index.html +1178 -10
- nautobot/project-static/docs/development/model-features.html +1178 -10
- nautobot/project-static/docs/development/natural-keys.html +1178 -10
- nautobot/project-static/docs/development/navigation-menu.html +1215 -125
- nautobot/project-static/docs/development/react-ui.html +4199 -0
- nautobot/project-static/docs/development/release-checklist.html +1178 -10
- nautobot/project-static/docs/development/role-internals.html +1179 -12
- nautobot/project-static/docs/development/style-guide.html +1188 -19
- nautobot/project-static/docs/development/templates.html +1178 -10
- nautobot/project-static/docs/development/testing.html +1178 -10
- nautobot/project-static/docs/development/user-preferences.html +1178 -10
- nautobot/project-static/docs/docker/index.html +1178 -10
- nautobot/project-static/docs/index.html +1183 -12
- nautobot/project-static/docs/installation/centos.html +1178 -10
- nautobot/project-static/docs/installation/external-authentication.html +1178 -10
- nautobot/project-static/docs/installation/http-server.html +1178 -10
- nautobot/project-static/docs/installation/index.html +1178 -10
- nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
- nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
- nautobot/project-static/docs/installation/nautobot.html +1179 -11
- nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
- nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
- nautobot/project-static/docs/installation/services.html +1178 -10
- nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
- nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
- nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
- nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
- nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
- nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
- nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
- nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
- nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
- nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
- nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
- nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
- nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
- nautobot/project-static/docs/installation/ubuntu.html +1178 -10
- nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
- nautobot/project-static/docs/installation/upgrading.html +1178 -10
- nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
- nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
- nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
- nautobot/project-static/docs/models/circuits/provider.html +1293 -103
- nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
- nautobot/project-static/docs/models/dcim/cable.html +1324 -103
- nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
- nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
- nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/device.html +1326 -132
- nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
- nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
- nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
- nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
- nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/interface.html +1293 -103
- nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
- nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
- nautobot/project-static/docs/models/dcim/location.html +1293 -103
- nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
- nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
- nautobot/project-static/docs/models/dcim/platform.html +1272 -82
- nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
- nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
- nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
- nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
- nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
- nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
- nautobot/project-static/docs/models/dcim/rack.html +1272 -82
- nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
- nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
- nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
- nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
- nautobot/project-static/docs/models/dcim/region.html +1178 -10
- nautobot/project-static/docs/models/dcim/site.html +1178 -10
- nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
- nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
- nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
- nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
- nautobot/project-static/docs/models/extras/customfield.html +1180 -12
- nautobot/project-static/docs/models/extras/customlink.html +1180 -12
- nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
- nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
- nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
- nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
- nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
- nautobot/project-static/docs/models/extras/job.html +1277 -86
- nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
- nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
- nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
- nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
- nautobot/project-static/docs/models/extras/note.html +1180 -12
- nautobot/project-static/docs/models/extras/relationship.html +1182 -14
- nautobot/project-static/docs/models/extras/role.html +1320 -86
- nautobot/project-static/docs/models/extras/secret.html +1314 -86
- nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
- nautobot/project-static/docs/models/extras/status.html +1188 -59
- nautobot/project-static/docs/models/extras/tag.html +1180 -12
- nautobot/project-static/docs/models/extras/webhook.html +1180 -12
- nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
- nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
- nautobot/project-static/docs/models/ipam/rir.html +1276 -86
- nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
- nautobot/project-static/docs/models/ipam/service.html +1276 -86
- nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
- nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
- nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
- nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
- nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
- nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
- nautobot/project-static/docs/models/users/token.html +1276 -86
- nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
- nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
- nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
- nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
- nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/plugins/development.html +1726 -495
- nautobot/project-static/docs/plugins/index.html +1178 -10
- nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
- nautobot/project-static/docs/release-notes/index.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
- nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
- nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
- nautobot/project-static/docs/requirements.txt +1 -0
- nautobot/project-static/docs/rest-api/authentication.html +1179 -11
- nautobot/project-static/docs/rest-api/filtering.html +1178 -10
- nautobot/project-static/docs/rest-api/overview.html +1841 -446
- nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +197 -187
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
- nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
- nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
- nautobot/project-static/docs/user-guides/graphql.html +1178 -10
- nautobot/project-static/docs/user-guides/relationships.html +1178 -10
- nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
- nautobot/project-static/js/forms.js +16 -9
- nautobot/project-static/js/theme.js +5 -0
- nautobot/tenancy/api/serializers.py +4 -32
- nautobot/tenancy/api/urls.py +1 -1
- nautobot/tenancy/forms.py +0 -28
- nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
- nautobot/tenancy/models.py +0 -25
- nautobot/tenancy/navigation.py +6 -39
- nautobot/tenancy/templates/tenancy/tenant.html +12 -12
- nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
- nautobot/tenancy/tests/test_api.py +1 -3
- nautobot/tenancy/tests/test_filters.py +10 -5
- nautobot/tenancy/views.py +0 -2
- nautobot/ui/.eslintignore +6 -0
- nautobot/ui/.gitignore +10 -0
- nautobot/ui/.prettierignore +9 -0
- nautobot/ui/.prettierrc +4 -0
- nautobot/ui/README.md +33 -0
- nautobot/ui/app_imports.js.j2 +7 -0
- nautobot/ui/craco.config.js +46 -0
- nautobot/ui/jsconfig-base.json +11 -0
- nautobot/ui/jsconfig.json +5 -0
- nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
- nautobot/ui/package-lock.json +21451 -0
- nautobot/ui/package.json +70 -0
- nautobot/ui/public/index.html +47 -0
- nautobot/ui/public/logo192.png +0 -0
- nautobot/ui/public/logo512.png +0 -0
- nautobot/ui/public/manifest.json +25 -0
- nautobot/ui/public/nautobot_logo.svg +131 -0
- nautobot/ui/public/robots.txt +3 -0
- nautobot/ui/src/App.js +71 -0
- nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
- nautobot/ui/src/components/AppTab.js +40 -0
- nautobot/ui/src/components/Apps.js +60 -0
- nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
- nautobot/ui/src/components/HomePanel.js +58 -0
- nautobot/ui/src/components/JobHistoryTable.js +78 -0
- nautobot/ui/src/components/Layout.js +53 -0
- nautobot/ui/src/components/LoadingWidget.js +25 -0
- nautobot/ui/src/components/Navbar.js +116 -0
- nautobot/ui/src/components/NotificationPopover.js +27 -0
- nautobot/ui/src/components/ObjectListTable.js +209 -0
- nautobot/ui/src/components/ReferenceDataTag.js +35 -0
- nautobot/ui/src/components/RouterButton.js +10 -0
- nautobot/ui/src/components/RouterLink.js +10 -0
- nautobot/ui/src/components/SidebarNav.js +147 -0
- nautobot/ui/src/components/Table.js +48 -0
- nautobot/ui/src/components/TableItem.js +71 -0
- nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
- nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
- nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
- nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
- nautobot/ui/src/components/__tests__/Table.test.js +36 -0
- nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
- nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
- nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
- nautobot/ui/src/components/pagination.js +93 -0
- nautobot/ui/src/components/paginator.js +79 -0
- nautobot/ui/src/components/paginator_form.js +43 -0
- nautobot/ui/src/components/usePagination.js +57 -0
- nautobot/ui/src/constants/apiPath.js +10 -0
- nautobot/ui/src/constants/icons.js +15 -0
- nautobot/ui/src/constants/size.js +15 -0
- nautobot/ui/src/index.js +65 -0
- nautobot/ui/src/reportWebVitals.js +15 -0
- nautobot/ui/src/router.js +77 -0
- nautobot/ui/src/utils/api.js +131 -0
- nautobot/ui/src/utils/app-import.js +15 -0
- nautobot/ui/src/utils/color.js +15 -0
- nautobot/ui/src/utils/date.js +14 -0
- nautobot/ui/src/utils/index.js +15 -0
- nautobot/ui/src/utils/navigation.js +32 -0
- nautobot/ui/src/utils/session.js +64 -0
- nautobot/ui/src/utils/store.js +242 -0
- nautobot/ui/src/utils/string.js +6 -0
- nautobot/ui/src/utils/url.js +4 -0
- nautobot/ui/src/views/Home.js +138 -0
- nautobot/ui/src/views/InstalledApps.js +80 -0
- nautobot/ui/src/views/Login.js +48 -0
- nautobot/ui/src/views/Logout.js +20 -0
- nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
- nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
- nautobot/ui/src/views/__tests__/Login.test.js +15 -0
- nautobot/ui/src/views/generic/GenericView.js +142 -0
- nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
- nautobot/ui/src/views/generic/ObjectList.js +127 -0
- nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
- nautobot/users/admin.py +1 -1
- nautobot/users/api/serializers.py +51 -61
- nautobot/users/api/urls.py +1 -1
- nautobot/users/api/views.py +53 -2
- nautobot/users/tests/test_api.py +110 -25
- nautobot/virtualization/api/serializers.py +18 -130
- nautobot/virtualization/api/urls.py +1 -1
- nautobot/virtualization/api/views.py +1 -22
- nautobot/virtualization/forms.py +13 -99
- nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
- nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
- nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
- nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
- nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
- nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
- nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
- nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
- nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
- nautobot/virtualization/models.py +31 -123
- nautobot/virtualization/navigation.py +18 -99
- nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
- nautobot/virtualization/tests/test_api.py +25 -26
- nautobot/virtualization/tests/test_filters.py +41 -15
- nautobot/virtualization/tests/test_models.py +31 -7
- nautobot/virtualization/tests/test_views.py +42 -25
- nautobot/virtualization/views.py +7 -6
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
- nautobot/circuits/api/nested_serializers.py +0 -69
- nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
- nautobot/dcim/api/nested_serializers.py +0 -356
- nautobot/dcim/templates/dcim/device_import.html +0 -5
- nautobot/dcim/templates/dcim/device_import_child.html +0 -5
- nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
- nautobot/extras/api/nested_serializers.py +0 -353
- nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
- nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
- nautobot/extras/reports.py +0 -60
- nautobot/extras/scripts.py +0 -72
- nautobot/extras/tests/example_jobs/script_variables.py +0 -67
- nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
- nautobot/extras/tests/example_jobs/test_fail.py +0 -16
- nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
- nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
- nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
- nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
- nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
- nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
- nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
- nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
- nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
- nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
- nautobot/extras/tests/example_jobs/test_pass.py +0 -19
- nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
- nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
- nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
- nautobot/ipam/api/nested_serializers.py +0 -159
- nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
- nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
- nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
- nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
- nautobot/tenancy/api/nested_serializers.py +0 -31
- nautobot/users/api/nested_serializers.py +0 -67
- nautobot/virtualization/api/nested_serializers.py +0 -65
- /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
- /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -11,8 +11,11 @@ Jobs are a way for users to execute custom logic on demand from within the Nauto
|
|
|
11
11
|
|
|
12
12
|
...and so on. Jobs are Python code and exist outside of the official Nautobot code base, so they can be updated and changed without interfering with the core Nautobot installation. And because they're completely customizable, there's practically no limit to what a job can accomplish.
|
|
13
13
|
|
|
14
|
+
+/- 2.0.0
|
|
15
|
+
Backwards compatibility with NetBox scripts and reports has been removed. This includes removal of automatic calls to the `post_run()` and `test_*()` methods.
|
|
16
|
+
|
|
14
17
|
!!! note
|
|
15
|
-
Jobs unify and supersede the functionality previously provided in NetBox by "custom scripts" and "reports".
|
|
18
|
+
Jobs unify and supersede the functionality previously provided in NetBox by "custom scripts" and "reports". User input is supported via [job variables](#variables).
|
|
16
19
|
|
|
17
20
|
## Writing Jobs
|
|
18
21
|
|
|
@@ -31,10 +34,14 @@ In any case, each module holds one or more Jobs (Python classes), each of which
|
|
|
31
34
|
|
|
32
35
|
For example, we can create a module named `devices.py` to hold all of our jobs which pertain to devices in Nautobot. Within that module, we might define several jobs. Each job is defined as a Python class inheriting from `extras.jobs.Job`, which provides the base functionality needed to accept user input and log activity.
|
|
33
36
|
|
|
37
|
+
+/- 2.0.0
|
|
38
|
+
All job classes must now be registered with `nautobot.core.celery.register_jobs` on module import. For plugins providing jobs, the module containing the jobs must be imported by the plugin's `__init__.py` and the `register_jobs` method called at import time. The `register_jobs` method accepts one or more job classes as arguments.
|
|
39
|
+
|
|
34
40
|
!!! warning
|
|
35
41
|
Make sure you are *not* inheriting `extras.jobs.models.Job` instead, otherwise Django will think you want to define a new database model.
|
|
36
42
|
|
|
37
43
|
```python
|
|
44
|
+
from nautobot.core.celery import register_jobs
|
|
38
45
|
from nautobot.extras.jobs import Job
|
|
39
46
|
|
|
40
47
|
class CreateDevices(Job):
|
|
@@ -45,20 +52,15 @@ class DeviceConnectionsReport(Job):
|
|
|
45
52
|
|
|
46
53
|
class DeviceIPsReport(Job):
|
|
47
54
|
...
|
|
55
|
+
|
|
56
|
+
register_jobs(CreateDevices, DeviceConnectionsReport, DeviceIPsReport)
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
Each job class will implement some or all of the following components:
|
|
51
60
|
|
|
52
61
|
* Module and class attributes, providing for default behavior, documentation and discoverability
|
|
53
62
|
* a set of variables for user input via the Nautobot UI (if your job requires any user inputs)
|
|
54
|
-
* a `run()` method, which is
|
|
55
|
-
* any number of `test_*()` methods, which will be invoked next in order of declaration. Log messages generated by the job will be grouped together by the test method they were invoked from.
|
|
56
|
-
* a `post_run()` method, which is executed last and can be used to handle any necessary cleanup or final events (such as sending an email or triggering a webhook). The status of the overall job is available at this time as `self.failed` and the `JobResult` data object is available as `self.result`.
|
|
57
|
-
|
|
58
|
-
You can implement the entire job within the `run()` function, but for more complex jobs, you may want to provide more granularity in the output and logging of activity. For this purpose, you can implement portions of the logic as `test_*()` methods (i.e., methods whose name begins with `test_*`) and/or a `post_run()` method. Log messages generated by the job logging APIs (more below on this topic) will be grouped together according to their base method (`run`, `test_a`, `test_b`, ..., `post_run`) which can aid in understanding the operation of the job.
|
|
59
|
-
|
|
60
|
-
!!! note
|
|
61
|
-
Your job can of course define additional Python methods to compartmentalize and reuse logic as required; however the `run`, `test_*`, and `post_run` methods are the only ones that will be automatically invoked by Nautobot.
|
|
63
|
+
* a `run()` method, which is the only required attribute on a Job class and receives the user input values, if any
|
|
62
64
|
|
|
63
65
|
It's important to understand that jobs execute on the server asynchronously as background tasks; they log messages and report their status to the database by updating [`JobResult`](../models/extras/jobresult.md) records and creating [`JobLogEntry`](../models/extras/joblogentry.md) records.
|
|
64
66
|
|
|
@@ -112,16 +114,19 @@ Default: `False`
|
|
|
112
114
|
|
|
113
115
|
A boolean that will mark this job as requiring approval from another user to be run. For more details on approvals, [please refer to the section on scheduling and approvals](./job-scheduling-and-approvals.md).
|
|
114
116
|
|
|
115
|
-
#### `
|
|
117
|
+
#### `dryrun_default`
|
|
116
118
|
|
|
117
|
-
|
|
119
|
+
+/- 2.0.0
|
|
120
|
+
The `commit_default` field was renamed to `dryrun_default` and the default value was changed from `True` to `False`. The `commit` functionality that provided an automatic rollback of database changes if the job failed was removed. The `dryrun` functionality was added to provide a way to bypass job approval if a job implements a [`DryRunVar`](#dryrunvar).
|
|
121
|
+
|
|
122
|
+
Default: `False`
|
|
118
123
|
|
|
119
|
-
The checkbox to
|
|
124
|
+
The checkbox to enable dryrun when executing a job is unchecked by default in the Nautobot UI. You can set `dryrun_default` to `True` under the `Meta` class if you want this option to instead be checked by default.
|
|
120
125
|
|
|
121
126
|
```python
|
|
122
127
|
class MyJob(Job):
|
|
123
128
|
class Meta:
|
|
124
|
-
|
|
129
|
+
dryrun_default = True
|
|
125
130
|
```
|
|
126
131
|
|
|
127
132
|
#### `field_order`
|
|
@@ -163,9 +168,12 @@ Important notes about hidden jobs:
|
|
|
163
168
|
|
|
164
169
|
+++ 1.1.0
|
|
165
170
|
|
|
171
|
+
+/- 2.0.0
|
|
172
|
+
The `read_only` flag no longer changes the behavior of Nautobot core and is up to the job author to decide whether their job should be considered read only.
|
|
173
|
+
|
|
166
174
|
Default: `False`
|
|
167
175
|
|
|
168
|
-
A boolean that
|
|
176
|
+
A boolean that can be set by the job author to indicate that the job does not make any changes to the environment. What behavior makes each job "read only" is up to the individual job author to decide. Note that user input may still be optionally collected with read-only jobs via job variables, as described below.
|
|
169
177
|
|
|
170
178
|
#### `soft_time_limit`
|
|
171
179
|
|
|
@@ -185,7 +193,7 @@ class ExampleJobWithSoftTimeLimit(Job):
|
|
|
185
193
|
description = "Set a soft time limit of 10 seconds`"
|
|
186
194
|
soft_time_limit = 10
|
|
187
195
|
|
|
188
|
-
def run(self
|
|
196
|
+
def run(self):
|
|
189
197
|
try:
|
|
190
198
|
# code which might take longer than 10 seconds to run
|
|
191
199
|
job_code()
|
|
@@ -250,7 +258,7 @@ class ExampleJobWithHardTimeLimit(Job):
|
|
|
250
258
|
description = "Set a hard time limit of 10 seconds`"
|
|
251
259
|
time_limit = 10
|
|
252
260
|
|
|
253
|
-
def run(self
|
|
261
|
+
def run(self):
|
|
254
262
|
# code which might take longer than 10 seconds to run
|
|
255
263
|
# this code will fail silently if the time_limit is exceeded
|
|
256
264
|
job_code()
|
|
@@ -271,7 +279,7 @@ class CreateDevices(Job):
|
|
|
271
279
|
var2 = IntegerVar(...)
|
|
272
280
|
var3 = ObjectVar(...)
|
|
273
281
|
|
|
274
|
-
def run(self,
|
|
282
|
+
def run(self, var1, var2, var3):
|
|
275
283
|
...
|
|
276
284
|
```
|
|
277
285
|
|
|
@@ -312,6 +320,10 @@ Stores a numeric integer. Options include:
|
|
|
312
320
|
|
|
313
321
|
A true/false flag. This field has no options beyond the defaults listed above.
|
|
314
322
|
|
|
323
|
+
#### `DryRunVar`
|
|
324
|
+
|
|
325
|
+
A true/false flag with special handling for jobs that require approval. If `dryrun = DryRunVar()` is declared on a job class, approval may be bypassed if `dryrun` is set to `True` on job execution.
|
|
326
|
+
|
|
315
327
|
#### `ChoiceVar`
|
|
316
328
|
|
|
317
329
|
A set of choices from which the user can select one.
|
|
@@ -364,20 +376,20 @@ device = ObjectVar(
|
|
|
364
376
|
)
|
|
365
377
|
```
|
|
366
378
|
|
|
367
|
-
Multiple values can be specified by assigning a list to the dictionary key. It is also possible to reference the value of other fields in the form by prepending a dollar sign (`$`) to the variable's name. The keys you can use in this dictionary are the same ones that are available in the REST API - as an example it is also possible to filter the `
|
|
379
|
+
Multiple values can be specified by assigning a list to the dictionary key. It is also possible to reference the value of other fields in the form by prepending a dollar sign (`$`) to the variable's name. The keys you can use in this dictionary are the same ones that are available in the REST API - as an example it is also possible to filter the `Location` `ObjectVar` for its `location_type` and `tenant_group`.
|
|
368
380
|
|
|
369
381
|
```python
|
|
370
|
-
|
|
371
|
-
model=
|
|
382
|
+
location_type = ObjectVar(
|
|
383
|
+
model=LocationType
|
|
372
384
|
)
|
|
373
385
|
tenant_group = ObjectVar(
|
|
374
386
|
model=TenantGroup
|
|
375
387
|
)
|
|
376
|
-
|
|
377
|
-
model=
|
|
388
|
+
location = ObjectVar(
|
|
389
|
+
model=Location,
|
|
378
390
|
query_params={
|
|
379
|
-
|
|
380
|
-
|
|
391
|
+
"location_type": "$location_type",
|
|
392
|
+
"tenant_group": "$tenant_group"
|
|
381
393
|
}
|
|
382
394
|
)
|
|
383
395
|
```
|
|
@@ -407,10 +419,7 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a
|
|
|
407
419
|
|
|
408
420
|
### The `run()` Method
|
|
409
421
|
|
|
410
|
-
The `run()` method
|
|
411
|
-
|
|
412
|
-
1. `data` - A dictionary which will contain all of the variable data passed in by the user (via the web UI or REST API)
|
|
413
|
-
2. `commit` - A boolean indicating whether database changes should be committed. If this is `False`, even if your Job attempts to make database changes, they will be automatically rolled back when the Job completes.
|
|
422
|
+
The `run()` method must be implemented. After the `self` argument, it should accept keyword arguments for any variables defined on the job:
|
|
414
423
|
|
|
415
424
|
```python
|
|
416
425
|
from nautobot.extras.jobs import Job, StringVar, IntegerVar, ObjectVar
|
|
@@ -420,20 +429,11 @@ class CreateDevices(Job):
|
|
|
420
429
|
var2 = IntegerVar(...)
|
|
421
430
|
var3 = ObjectVar(...)
|
|
422
431
|
|
|
423
|
-
def run(self,
|
|
432
|
+
def run(self, var1, var2, var3):
|
|
424
433
|
...
|
|
425
434
|
```
|
|
426
435
|
|
|
427
|
-
Again, defining user variables is totally optional; you may create a job with
|
|
428
|
-
|
|
429
|
-
!!! note
|
|
430
|
-
The `test_*()` and `post_run()` methods do not accept any arguments; if you need to access user `data` or the `commit` flag, your `run()` method is responsible for storing these values in the job instance, such as:
|
|
431
|
-
|
|
432
|
-
```
|
|
433
|
-
def run(self, data, commit):
|
|
434
|
-
self.data = data
|
|
435
|
-
self.commit = commit
|
|
436
|
-
```
|
|
436
|
+
Again, defining user variables is totally optional; you may create a job with a `run()` method with only the `self` argument if no user input is needed.
|
|
437
437
|
|
|
438
438
|
!!! warning
|
|
439
439
|
When writing Jobs that create and manipulate data it is recommended to make use of the `validated_save()` convenience method which exists on all core models. This method saves the instance data but first enforces model validation logic. Simply calling `save()` on the model instance **does not** enforce validation automatically and may lead to bad data. See the development [best practices](../development/best-practices.md).
|
|
@@ -441,54 +441,82 @@ Again, defining user variables is totally optional; you may create a job with ju
|
|
|
441
441
|
!!! warning
|
|
442
442
|
The Django ORM provides methods to create/edit many objects at once, namely `bulk_create()` and `update()`. These are best avoided in most cases as they bypass a model's built-in validation and can easily lead to database corruption if not used carefully.
|
|
443
443
|
|
|
444
|
-
|
|
444
|
+
--- 2.0.0
|
|
445
|
+
The NetBox backwards compatible `test_*()` and `post_run()` methods have been removed.
|
|
446
|
+
|
|
447
|
+
### Logging
|
|
445
448
|
|
|
446
|
-
|
|
449
|
+
+/- 2.0.0
|
|
447
450
|
|
|
448
|
-
|
|
451
|
+
Messages logged from a job's logger will be stored in [`JobLogEntry`](../models/extras/joblogentry.md) records associated with the current [`JobResult`](../models/extras/jobresult.md).
|
|
449
452
|
|
|
450
|
-
|
|
453
|
+
The logger can be accessed either by using the `logger` property on the job class or `nautobot.extras.jobs.get_task_logger(__name__)`. Both will return the same logger instance. For more information on the standard Python logging module, see the [Python documentation](https://docs.python.org/3/library/logging.html).
|
|
451
454
|
|
|
452
|
-
|
|
455
|
+
An optional `grouping` and/or `object` may be provided in log messages by passing them in the log function call's `extra` kwarg. If a `grouping` is not provided it will default to the function name that logged the message. The `object` will default to `None`.
|
|
453
456
|
|
|
454
|
-
|
|
457
|
+
!!! example
|
|
458
|
+
```py
|
|
459
|
+
from nautobot.extras.jobs import BaseJob
|
|
455
460
|
|
|
456
|
-
|
|
461
|
+
class MyJob(BaseJob):
|
|
462
|
+
def run(self):
|
|
463
|
+
logger.info("This job is running!", extra={"grouping": "myjobisrunning", "object": self.job_result})
|
|
464
|
+
```
|
|
457
465
|
|
|
458
|
-
|
|
459
|
-
* `self.log_debug(message)`
|
|
460
|
-
* `self.log_success(obj=None, message=None)`
|
|
461
|
-
* `self.log_info(obj=None, message=None)`
|
|
462
|
-
* `self.log_warning(obj=None, message=None)`
|
|
463
|
-
* `self.log_failure(obj=None, message=None)`
|
|
466
|
+
To skip writing a log entry to the database, set the `skip_db_logging` key in the "extra" kwarg to `True` when calling the log function. The output will still be written to the console.
|
|
464
467
|
|
|
465
|
-
|
|
468
|
+
!!! example
|
|
469
|
+
```py
|
|
470
|
+
from nautobot.extras.jobs import BaseJob
|
|
466
471
|
|
|
467
|
-
|
|
472
|
+
class MyJob(BaseJob):
|
|
473
|
+
def run(self):
|
|
474
|
+
logger.info("This job is running!", extra={"skip_db_logging": True})
|
|
475
|
+
```
|
|
468
476
|
|
|
469
477
|
Markdown rendering is supported for log messages.
|
|
470
478
|
|
|
471
479
|
+/- 1.3.4
|
|
472
480
|
As a security measure, the `message` passed to any of these methods will be passed through the `nautobot.core.utils.logging.sanitize()` function in an attempt to strip out information such as usernames/passwords that should not be saved to the logs. This is of course best-effort only, and Job authors should take pains to ensure that such information is not passed to the logging APIs in the first place. The set of redaction rules used by the `sanitize()` function can be configured as [settings.SANITIZER_PATTERNS](../configuration/optional-settings.md#sanitizer_patterns).
|
|
473
481
|
|
|
474
|
-
|
|
475
|
-
|
|
482
|
+
+/- 2.0.0
|
|
483
|
+
The Job class logging functions (example: `self.log(message)`, `self.log_success(obj=None, message=message)`, etc) have been removed. Also, the convenience method to mark a job as failed, `log_failure()`, has been removed. To replace the functionality of this method, you can log an error message with `self.logger.error()` and then raise an exception to fail the job. Note that it is no longer possible to manually set the job result status as failed without raising an exception in the job.
|
|
476
484
|
|
|
477
485
|
+/- 2.0.0
|
|
478
486
|
The `AbortTransaction` class was moved from the `nautobot.utilities.exceptions` module to `nautobot.core.exceptions`.
|
|
479
487
|
|
|
480
|
-
###
|
|
488
|
+
### Marking a Job as Failed
|
|
489
|
+
|
|
490
|
+
To mark a job as failed, raise an exception from within the `run()` method. The exception message will be logged to the traceback of the job result. The job result status will be set to `failed`. To output a job log message you can use the `self.logger.error()` method.
|
|
491
|
+
|
|
492
|
+
```python
|
|
481
493
|
|
|
482
|
-
|
|
494
|
+
As an example, the following job will fail if the user does not put the word "Taco" in `var1`:
|
|
483
495
|
|
|
484
496
|
```python
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
497
|
+
from nautobot.extras.jobs import Job, StringVar
|
|
498
|
+
|
|
499
|
+
class MyJob(Job):
|
|
500
|
+
var1 = StringVar(...)
|
|
501
|
+
|
|
502
|
+
def run(self, var1):
|
|
503
|
+
if var1 != "Taco":
|
|
504
|
+
self.logger.error("var1 must be 'Taco'")
|
|
505
|
+
raise Exception("Argument input validation failed.")
|
|
489
506
|
```
|
|
490
507
|
|
|
491
|
-
|
|
508
|
+
### Accessing User and Job Result
|
|
509
|
+
|
|
510
|
+
+/- 2.0.0
|
|
511
|
+
The web request is no longer accessible to running jobs.
|
|
512
|
+
|
|
513
|
+
The user that initiated the job and the job result associated to the job can be accessed through properties on the job class:
|
|
514
|
+
|
|
515
|
+
```py
|
|
516
|
+
username = self.user.username
|
|
517
|
+
job_result_id = self.job_result.id
|
|
518
|
+
self.logger.info("Job %s initiated by user %s is running.", job_result_id, username)
|
|
519
|
+
```
|
|
492
520
|
|
|
493
521
|
### Reading Data from Files
|
|
494
522
|
|
|
@@ -520,10 +548,9 @@ An administrator or user with `extras.change_job` permission can also edit a Job
|
|
|
520
548
|
* `name`
|
|
521
549
|
* `description`
|
|
522
550
|
* `approval_required`
|
|
523
|
-
* `
|
|
551
|
+
* `dryrun_default`
|
|
524
552
|
* `has_sensitive_variables`
|
|
525
553
|
* `hidden`
|
|
526
|
-
* `read_only`
|
|
527
554
|
* `soft_time_limit`
|
|
528
555
|
* `time_limit`
|
|
529
556
|
* `task_queues`
|
|
@@ -568,9 +595,12 @@ Once a job has been run, the latest [`JobResult`](../models/extras/jobresult.md)
|
|
|
568
595
|
|
|
569
596
|
### Via the API
|
|
570
597
|
|
|
571
|
-
|
|
598
|
+
--- 2.0.0
|
|
599
|
+
The `commit` parameter was removed. All job input should be provided via the `data` parameter.
|
|
572
600
|
|
|
573
|
-
|
|
601
|
+
To run a job via the REST API, issue a POST request to the job's endpoint `/api/extras/jobs/<uuid>/run/`. You can optionally provide JSON data to specify any required user input `data`, optional `task_queue`, and/or provide optional scheduling information as described in [the section on scheduling and approvals](./job-scheduling-and-approvals.md).
|
|
602
|
+
|
|
603
|
+
For example, to run a job with no user inputs:
|
|
574
604
|
|
|
575
605
|
```no-highlight
|
|
576
606
|
curl -X POST \
|
|
@@ -580,7 +610,7 @@ curl -X POST \
|
|
|
580
610
|
http://nautobot/api/extras/jobs/$JOB_ID/run/
|
|
581
611
|
```
|
|
582
612
|
|
|
583
|
-
Or to run a job that expects user inputs
|
|
613
|
+
Or to run a job that expects user inputs:
|
|
584
614
|
|
|
585
615
|
```no-highlight
|
|
586
616
|
curl -X POST \
|
|
@@ -588,7 +618,7 @@ curl -X POST \
|
|
|
588
618
|
-H "Content-Type: application/json" \
|
|
589
619
|
-H "Accept: application/json; version=1.3; indent=4" \
|
|
590
620
|
http://nautobot/api/extras/jobs/$JOB_ID/run/ \
|
|
591
|
-
--data '{"data": {"string_variable": "somevalue", "integer_variable": 123}
|
|
621
|
+
--data '{"data": {"string_variable": "somevalue", "integer_variable": 123}}'
|
|
592
622
|
```
|
|
593
623
|
|
|
594
624
|
When providing input data, it is possible to specify complex values contained in `ObjectVar`s, `MultiObjectVar`s, and `IPAddressVar`s.
|
|
@@ -599,7 +629,7 @@ When providing input data, it is possible to specify complex values contained in
|
|
|
599
629
|
|
|
600
630
|
#### Jobs with Files
|
|
601
631
|
|
|
602
|
-
To run a job that contains `FileVar` inputs via the REST API, you must use `multipart/form-data` content type requests instead of `application/json`. This also requires a slightly different request payload than the example above. The `
|
|
632
|
+
To run a job that contains `FileVar` inputs via the REST API, you must use `multipart/form-data` content type requests instead of `application/json`. This also requires a slightly different request payload than the example above. The `task_queue` and `schedule` data are flattened and prefixed with underscore to differentiate them from job-specific data. Job specific data is also flattened and not located under the top-level `data` dictionary key.
|
|
603
633
|
|
|
604
634
|
An example of running a job with both `FileVar` (named `myfile`) and `StringVar` (named `interval`) input:
|
|
605
635
|
|
|
@@ -609,7 +639,6 @@ curl -X POST \
|
|
|
609
639
|
-H 'Content-Type: multipart/form-data' \
|
|
610
640
|
-H "Accept: application/json; version=1.3; indent=4" \
|
|
611
641
|
'http://nautobot/api/extras/jobs/$JOB_ID/run/' \
|
|
612
|
-
-F '_commit="true"' \
|
|
613
642
|
-F '_schedule_interval="immediately"' \
|
|
614
643
|
-F '_schedule_start_time="2022-10-18T17:31:23.698Z"' \
|
|
615
644
|
-F 'interval="3"' \
|
|
@@ -621,7 +650,7 @@ curl -X POST \
|
|
|
621
650
|
Jobs can be run from the CLI by invoking the management command:
|
|
622
651
|
|
|
623
652
|
```no-highlight
|
|
624
|
-
nautobot-server runjob [--username <username>] [--
|
|
653
|
+
nautobot-server runjob [--username <username>] [--local] [--data <data>] <class_path>
|
|
625
654
|
```
|
|
626
655
|
|
|
627
656
|
!!! note
|
|
@@ -639,7 +668,7 @@ nautobot-server runjob --username myusername local/example/MyJobWithNoVars
|
|
|
639
668
|
```
|
|
640
669
|
|
|
641
670
|
!!! warning
|
|
642
|
-
The `--username <username>`
|
|
671
|
+
The `--username <username>` must be supplied to specify the user that will be identified as the requester of the job.
|
|
643
672
|
|
|
644
673
|
Note that `nautobot-server` commands, like all management commands and other direct interactions with the Django database, are not gated by the usual Nautobot user authentication flow. It is possible to specify any existing `--username` with the `nautobot-server runjob` command in order to impersonate any defined user in Nautobot. Use this power wisely and be cautious who you allow to access it.
|
|
645
674
|
|
|
@@ -651,12 +680,12 @@ While individual methods within your Job can and should be tested in isolation,
|
|
|
651
680
|
|
|
652
681
|
### Nautobot 1.3.3 and later
|
|
653
682
|
|
|
654
|
-
The simplest way to test the entire execution of Jobs from 1.3.3 on is via calling the `nautobot.core.testing.run_job_for_testing()` method, which is a helper wrapper around the `
|
|
683
|
+
The simplest way to test the entire execution of Jobs from 1.3.3 on is via calling the `nautobot.core.testing.run_job_for_testing()` method, which is a helper wrapper around the `JobResult.enqueue_job` function used to execute a Job via Nautobot's Celery worker process.
|
|
655
684
|
|
|
656
685
|
+/- 2.0.0
|
|
657
686
|
`run_job_for_testing` was moved from the `nautobot.utilities.testing` module to `nautobot.core.testing`.
|
|
658
687
|
|
|
659
|
-
Because of the way `run_job_for_testing` and more specifically
|
|
688
|
+
Because of the way `run_job_for_testing` and more specifically Celery tasks work, which is somewhat complex behind the scenes, you need to inherit from `nautobot.core.testing.TransactionTestCase` instead of `django.test.TestCase` (Refer to the [Django documentation](https://docs.djangoproject.com/en/stable/topics/testing/tools/#provided-test-case-classes) if you're interested in the differences between these classes - `TransactionTestCase` from Nautobot is a small wrapper around Django's `TransactionTestCase`).
|
|
660
689
|
|
|
661
690
|
When using `TransactionTestCase` (whether from Django or from Nautobot) each tests runs on a completely empty database. Furthermore, Nautobot requires new jobs to be enabled before they can run. Therefore, we need to make sure the job is enabled before each run which `run_job_for_testing` handles for us.
|
|
662
691
|
|
|
@@ -672,85 +701,48 @@ class MyJobTestCase(TransactionTestCase):
|
|
|
672
701
|
# Testing of Job "MyJob" in file "my_job_file.py" in $JOBS_ROOT
|
|
673
702
|
job = Job.objects.get(job_class_name="MyJob", module_name="my_job_file", source="local")
|
|
674
703
|
# or, job = Job.objects.get_for_class_path("local/my_job_file/MyJob")
|
|
675
|
-
job_result = run_job_for_testing(job,
|
|
704
|
+
job_result = run_job_for_testing(job, var1="abc", var2=123)
|
|
676
705
|
|
|
677
|
-
#
|
|
678
|
-
# but we can still inspect the logs created by running the job
|
|
706
|
+
# Inspect the logs created by running the job
|
|
679
707
|
log_entries = JobLogEntry.objects.filter(job_result=job_result)
|
|
680
708
|
for log_entry in log_entries:
|
|
681
709
|
self.assertEqual(log_entry.message, "...")
|
|
682
710
|
```
|
|
683
711
|
|
|
684
712
|
!!! tip
|
|
685
|
-
For more advanced examples
|
|
686
|
-
|
|
687
|
-
### Nautobot 1.3.2 and earlier (including 1.2)
|
|
713
|
+
For more advanced examples refer to the Nautobot source code, specifically `nautobot/extras/tests/test_jobs.py`.
|
|
688
714
|
|
|
689
|
-
|
|
715
|
+
## Debugging job performance
|
|
690
716
|
|
|
691
|
-
|
|
717
|
+
+++ 1.5.17
|
|
692
718
|
|
|
693
|
-
|
|
694
|
-
from django.conf import settings
|
|
695
|
-
|
|
696
|
-
if "job_logs" in settings.DATABASES:
|
|
697
|
-
settings.DATABASES["job_logs"] = settings.DATABASES["job_logs"].copy()
|
|
698
|
-
settings.DATABASES["job_logs"]["TEST"] = {"MIRROR": "default"}
|
|
699
|
-
```
|
|
719
|
+
Debugging the performance of Nautobot jobs can be tricky, because they are executed in the worker context. In order to gain extra visibility, [cProfile](https://docs.python.org/3/library/profile.html) can be used to profile the job execution.
|
|
700
720
|
|
|
701
|
-
|
|
721
|
+
The 'profile' form field on jobs is automatically available when the `DEBUG` settings is `True`. When you select that checkbox, a profiling report in the pstats format will be written to the file system of the environment where the job runs. Normally, this is on the file system of the worker process, but if you are using the `nautobot-server runjob` command with `--local`, it will end up in the file system of the web application itself. The path of the written file will be logged in the job.
|
|
702
722
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
from django.contrib.auth import get_user_model
|
|
706
|
-
from django.contrib.contenttypes.models import ContentType
|
|
723
|
+
!!! note
|
|
724
|
+
If you need to run this in an environment where `DEBUG` is `False`, you have the option of using `nautobot-server runjob` with the `--profile` flag. According to the docs, `cProfile` should have minimal impact on the performance of the job; still, proceed with caution when using this in a production environment.
|
|
707
725
|
|
|
708
|
-
|
|
709
|
-
from nautobot.extras.jobs import run_job
|
|
710
|
-
from nautobot.extras.models import JobResult, Job
|
|
711
|
-
|
|
712
|
-
def run_job_for_testing(job, data=None, commit=True, username="test-user"):
|
|
713
|
-
if data is None:
|
|
714
|
-
data = {}
|
|
715
|
-
user_model = get_user_model()
|
|
716
|
-
user, _ = user_model.objects.get_or_create(username=username, is_superuser=True, password="password")
|
|
717
|
-
job_result = JobResult.objects.create(
|
|
718
|
-
name=job.class_path,
|
|
719
|
-
obj_type=ContentType.objects.get_for_model(Job),
|
|
720
|
-
user=user,
|
|
721
|
-
task_id=uuid.uuid4(),
|
|
722
|
-
)
|
|
723
|
-
with web_request_context(user=user) as request:
|
|
724
|
-
run_job(data=data, request=request, commit=commit, job_result_pk=job_result.pk)
|
|
725
|
-
return job_result
|
|
726
|
-
```
|
|
726
|
+
### Reading profiling reports
|
|
727
727
|
|
|
728
|
-
|
|
728
|
+
A full description on how to deal with the output of `cProfile` can be found in the [Instant User's Manual](https://docs.python.org/3/library/profile.html#instant-user-s-manual), but here is something to get you started:
|
|
729
729
|
|
|
730
730
|
```python
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
from nautobot.extras.management import populate_status_choices
|
|
736
|
-
|
|
737
|
-
class MyJobTestCase(TransactionTestCase):
|
|
738
|
-
# 'job_logs' is a proxy connection to the same (default) database that's used exclusively for Job logging
|
|
739
|
-
if "job_logs" in settings.DATABASES:
|
|
740
|
-
databases = ("default", "job_logs")
|
|
741
|
-
|
|
742
|
-
def setUp(self):
|
|
743
|
-
super().setUp()
|
|
744
|
-
populate_status_choices(apps, None)
|
|
731
|
+
import pstats
|
|
732
|
+
job_result_uuid = "66b70231-002f-412b-8cc4-1cc9609c2c9b"
|
|
733
|
+
stats = pstats.Stats(f"/tmp/job-result-{job_result_uuid}.pstats")
|
|
734
|
+
stats.sort_stats(pstats.SortKey.CUMULATIVE).print_stats(10)
|
|
745
735
|
```
|
|
746
736
|
|
|
737
|
+
This will print the 10 functions that the job execution spent the most time in - adapt this to your needs!
|
|
738
|
+
|
|
747
739
|
## Example Jobs
|
|
748
740
|
|
|
749
|
-
### Creating objects for a planned
|
|
741
|
+
### Creating objects for a planned location
|
|
750
742
|
|
|
751
743
|
This job prompts the user for three variables:
|
|
752
744
|
|
|
753
|
-
* The name of the new
|
|
745
|
+
* The name of the new location
|
|
754
746
|
* The device model (a filtered list of defined device types)
|
|
755
747
|
* The number of access switches to create
|
|
756
748
|
|
|
@@ -758,78 +750,60 @@ These variables are presented as a web form to be completed by the user. Once su
|
|
|
758
750
|
|
|
759
751
|
```python
|
|
760
752
|
from django.contrib.contenttypes.models import ContentType
|
|
761
|
-
from django.utils.text import slugify
|
|
762
753
|
|
|
763
|
-
from nautobot.dcim.models import
|
|
764
|
-
from nautobot.extras.models import
|
|
765
|
-
from nautobot.extras.jobs import
|
|
754
|
+
from nautobot.dcim.models import Location, LocationType, Device, Manufacturer, DeviceType
|
|
755
|
+
from nautobot.extras.models import Status, Role
|
|
756
|
+
from nautobot.extras.jobs import Job, StringVar, IntegerVar, ObjectVar
|
|
766
757
|
|
|
767
758
|
|
|
768
759
|
class NewBranch(Job):
|
|
769
|
-
|
|
770
760
|
class Meta:
|
|
771
761
|
name = "New Branch"
|
|
772
|
-
description = "Provision a new branch
|
|
773
|
-
field_order = [
|
|
762
|
+
description = "Provision a new branch location"
|
|
763
|
+
field_order = ["location_name", "switch_count", "switch_model"]
|
|
774
764
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
)
|
|
778
|
-
switch_count = IntegerVar(
|
|
779
|
-
description="Number of access switches to create"
|
|
780
|
-
)
|
|
781
|
-
manufacturer = ObjectVar(
|
|
782
|
-
model=Manufacturer,
|
|
783
|
-
required=False
|
|
784
|
-
)
|
|
765
|
+
location_name = StringVar(description="Name of the new location")
|
|
766
|
+
switch_count = IntegerVar(description="Number of access switches to create")
|
|
767
|
+
manufacturer = ObjectVar(model=Manufacturer, required=False)
|
|
785
768
|
switch_model = ObjectVar(
|
|
786
|
-
description="Access switch model",
|
|
787
|
-
model=DeviceType,
|
|
788
|
-
query_params={
|
|
789
|
-
'manufacturer_id': '$manufacturer'
|
|
790
|
-
}
|
|
769
|
+
description="Access switch model", model=DeviceType, query_params={"manufacturer_id": "$manufacturer"}
|
|
791
770
|
)
|
|
792
771
|
|
|
793
|
-
def run(self,
|
|
794
|
-
STATUS_PLANNED = Status.objects.get(name=
|
|
772
|
+
def run(self, location_name, switch_count, switch_model):
|
|
773
|
+
STATUS_PLANNED = Status.objects.get(name="Planned")
|
|
795
774
|
|
|
796
|
-
# Create the new
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
775
|
+
# Create the new location
|
|
776
|
+
root_type = LocationType.objects.get_or_create(name="Campus")
|
|
777
|
+
location = Location(
|
|
778
|
+
name=location_name,
|
|
779
|
+
location_type=root_type,
|
|
800
780
|
status=STATUS_PLANNED,
|
|
801
781
|
)
|
|
802
|
-
|
|
803
|
-
self.
|
|
782
|
+
location.validated_save()
|
|
783
|
+
self.logger.info("Created new location", extra={"object": location})
|
|
804
784
|
|
|
805
785
|
# Create access switches
|
|
806
786
|
device_ct = ContentType.objects.get_for_model(Device)
|
|
807
|
-
switch_role = Role.objects.get(name=
|
|
787
|
+
switch_role = Role.objects.get(name="Access Switch")
|
|
808
788
|
switch_role.content_types.add(device_ct)
|
|
809
|
-
for i in range(1,
|
|
789
|
+
for i in range(1, switch_count + 1):
|
|
810
790
|
switch = Device(
|
|
811
|
-
device_type=
|
|
812
|
-
name=f
|
|
813
|
-
|
|
791
|
+
device_type=switch_model,
|
|
792
|
+
name=f"{location.name}-switch{i}",
|
|
793
|
+
location=location,
|
|
814
794
|
status=STATUS_PLANNED,
|
|
815
|
-
role=switch_role
|
|
795
|
+
role=switch_role,
|
|
816
796
|
)
|
|
817
797
|
switch.validated_save()
|
|
818
|
-
self.
|
|
798
|
+
self.logger.info("Created new switch", extra={"object": switch})
|
|
819
799
|
|
|
820
800
|
# Generate a CSV table of new devices
|
|
821
|
-
output = [
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
switch.device_type.manufacturer.name,
|
|
828
|
-
switch.device_type.model
|
|
829
|
-
]
|
|
830
|
-
output.append(','.join(attrs))
|
|
831
|
-
|
|
832
|
-
return '\n'.join(output)
|
|
801
|
+
output = ["name,make,model"]
|
|
802
|
+
for switch in Device.objects.filter(location=location):
|
|
803
|
+
attrs = [switch.name, switch.device_type.manufacturer.name, switch.device_type.model]
|
|
804
|
+
output.append(",".join(attrs))
|
|
805
|
+
|
|
806
|
+
return "\n".join(output)
|
|
833
807
|
```
|
|
834
808
|
|
|
835
809
|
### Device validation
|
|
@@ -849,19 +823,25 @@ class DeviceConnectionsReport(Job):
|
|
|
849
823
|
STATUS_ACTIVE = Status.objects.get(name='Active')
|
|
850
824
|
|
|
851
825
|
# Check that every console port for every active device has a connection defined.
|
|
852
|
-
for console_port in ConsolePort.objects.
|
|
826
|
+
for console_port in ConsolePort.objects.select_related('device').filter(device__status=STATUS_ACTIVE):
|
|
853
827
|
if console_port.connected_endpoint is None:
|
|
854
|
-
self.
|
|
855
|
-
|
|
856
|
-
|
|
828
|
+
self.logger.error(
|
|
829
|
+
"No console connection defined for %s",
|
|
830
|
+
console_port.name,
|
|
831
|
+
extra={"object": console_port.device},
|
|
857
832
|
)
|
|
858
833
|
elif not console_port.connection_status:
|
|
859
|
-
self.
|
|
860
|
-
|
|
861
|
-
|
|
834
|
+
self.logger.warning(
|
|
835
|
+
"Console connection for %s marked as planned",
|
|
836
|
+
console_port.name,
|
|
837
|
+
extra={"object": console_port.device},
|
|
862
838
|
)
|
|
863
839
|
else:
|
|
864
|
-
self.
|
|
840
|
+
self.logger.info(
|
|
841
|
+
"Console port %s has a connection defined",
|
|
842
|
+
console_port.name,
|
|
843
|
+
extra={"object": console_port.device},
|
|
844
|
+
)
|
|
865
845
|
|
|
866
846
|
def test_power_connections(self):
|
|
867
847
|
STATUS_ACTIVE = Status.objects.get(name='Active')
|
|
@@ -873,15 +853,17 @@ class DeviceConnectionsReport(Job):
|
|
|
873
853
|
if power_port.connected_endpoint is not None:
|
|
874
854
|
connected_ports += 1
|
|
875
855
|
if not power_port.connection_status:
|
|
876
|
-
self.
|
|
877
|
-
|
|
878
|
-
|
|
856
|
+
self.logger.warning(
|
|
857
|
+
"Power connection for %s marked as planned",
|
|
858
|
+
power_port.name,
|
|
859
|
+
extra={"object": device},
|
|
879
860
|
)
|
|
880
861
|
if connected_ports < 2:
|
|
881
|
-
self.
|
|
882
|
-
|
|
883
|
-
|
|
862
|
+
self.logger.error(
|
|
863
|
+
"%s connected power supplies found (2 needed)",
|
|
864
|
+
connected_ports,
|
|
865
|
+
extra={"object": device},
|
|
884
866
|
)
|
|
885
867
|
else:
|
|
886
|
-
self.
|
|
868
|
+
self.logger.info("At least two connected power supplies found", extra={"object": device})
|
|
887
869
|
```
|