nautobot 2.3.0b1__py3-none-any.whl → 2.3.2__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.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/cloud/factory.py +2 -0
- nautobot/cloud/filters.py +3 -0
- nautobot/cloud/forms.py +8 -2
- nautobot/cloud/migrations/0001_initial.py +1 -1
- nautobot/cloud/models.py +1 -2
- nautobot/cloud/tables.py +1 -17
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +1 -7
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +11 -0
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +4 -0
- nautobot/cloud/tests/test_filters.py +12 -0
- nautobot/cloud/tests/test_views.py +17 -0
- nautobot/cloud/views.py +1 -1
- nautobot/core/celery/__init__.py +5 -2
- nautobot/core/celery/schedulers.py +18 -0
- nautobot/core/filters.py +15 -1
- nautobot/core/forms/forms.py +10 -2
- nautobot/core/graphql/generators.py +2 -2
- nautobot/core/graphql/schema.py +6 -14
- nautobot/core/jobs/__init__.py +4 -1
- nautobot/core/management/commands/generate_test_data.py +2 -2
- nautobot/core/models/__init__.py +2 -2
- nautobot/core/settings.py +13 -2
- nautobot/core/settings.yaml +19 -5
- nautobot/core/tables.py +4 -1
- nautobot/core/templates/generic/object_retrieve.html +6 -6
- nautobot/core/templates/home.html +4 -3
- nautobot/core/templates/inc/computed_fields/panel_data.html +36 -24
- nautobot/core/templates/inc/object_details_advanced_panel.html +1 -1
- nautobot/core/templates/nautobot_config.py.j2 +15 -0
- nautobot/core/templatetags/buttons.py +1 -1
- nautobot/core/testing/filters.py +12 -1
- nautobot/core/tests/integration/test_general_functionality.py +1 -1
- nautobot/core/tests/test_jobs.py +74 -1
- nautobot/core/views/__init__.py +1 -1
- nautobot/core/views/generic.py +1 -1
- nautobot/core/views/mixins.py +1 -1
- nautobot/core/views/utils.py +11 -9
- nautobot/dcim/factory.py +7 -4
- nautobot/dcim/filters/__init__.py +4 -0
- nautobot/dcim/forms.py +24 -0
- nautobot/dcim/migrations/0061_module_models.py +1 -0
- nautobot/dcim/models/device_components.py +7 -0
- nautobot/dcim/models/devices.py +18 -19
- nautobot/dcim/models/racks.py +0 -1
- nautobot/dcim/tables/devices.py +24 -10
- nautobot/dcim/tables/devicetypes.py +1 -1
- nautobot/dcim/templates/dcim/device/base.html +1 -1
- nautobot/dcim/templates/dcim/device.html +15 -3
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +6 -0
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +17 -0
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +15 -3
- nautobot/dcim/tests/test_api.py +2 -0
- nautobot/dcim/tests/test_filters.py +14 -7
- nautobot/dcim/tests/test_forms.py +54 -0
- nautobot/dcim/tests/test_models.py +40 -1
- nautobot/dcim/tests/test_views.py +45 -2
- nautobot/dcim/utils.py +9 -6
- nautobot/dcim/views.py +4 -1
- nautobot/extras/api/serializers.py +2 -1
- nautobot/extras/api/views.py +7 -59
- nautobot/extras/factory.py +50 -12
- nautobot/extras/filters/__init__.py +18 -3
- nautobot/extras/forms/base.py +10 -4
- nautobot/extras/forms/forms.py +7 -0
- nautobot/extras/forms/mixins.py +2 -2
- nautobot/extras/homepage.py +12 -2
- nautobot/extras/jobs.py +2 -2
- nautobot/extras/management/__init__.py +3 -0
- nautobot/extras/migrations/0111_metadata.py +4 -4
- nautobot/extras/migrations/0114_computedfield_grouping.py +17 -0
- nautobot/extras/migrations/0115_scheduledjob_time_zone.py +23 -0
- nautobot/extras/models/customfields.py +54 -0
- nautobot/extras/models/jobs.py +105 -9
- nautobot/extras/models/metadata.py +18 -18
- nautobot/extras/models/models.py +2 -0
- nautobot/extras/signals.py +14 -1
- nautobot/extras/tables.py +77 -18
- nautobot/extras/templates/extras/computedfield.html +4 -0
- nautobot/extras/templates/extras/job_detail.html +11 -0
- nautobot/extras/templates/extras/scheduledjob.html +13 -2
- nautobot/extras/tests/test_api.py +33 -27
- nautobot/extras/tests/test_filters.py +57 -1
- nautobot/extras/tests/test_jobs.py +2 -2
- nautobot/extras/tests/test_models.py +319 -19
- nautobot/extras/tests/test_views.py +26 -5
- nautobot/extras/utils.py +35 -6
- nautobot/extras/views.py +35 -51
- nautobot/ipam/api/views.py +9 -2
- nautobot/ipam/choices.py +17 -0
- nautobot/ipam/factory.py +6 -0
- nautobot/ipam/filters.py +2 -2
- nautobot/ipam/forms.py +6 -4
- nautobot/ipam/migrations/0048_vrf_status.py +23 -0
- nautobot/ipam/migrations/0049_vrf_data_migration.py +25 -0
- nautobot/ipam/models.py +11 -20
- nautobot/ipam/querysets.py +26 -0
- nautobot/ipam/tables.py +7 -2
- nautobot/ipam/templates/ipam/vrf.html +4 -0
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -0
- nautobot/ipam/tests/test_api.py +33 -3
- nautobot/ipam/tests/test_models.py +89 -2
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/ipam/views.py +10 -15
- nautobot/project-static/css/base.css +7 -0
- nautobot/project-static/docs/404.html +18 -18
- nautobot/project-static/docs/apps/index.html +18 -18
- nautobot/project-static/docs/apps/nautobot-apps.html +18 -18
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +66 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +66 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +34 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +82 -63
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +75 -111
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +34 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +161 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +21 -19
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +34 -18
- nautobot/project-static/docs/development/apps/api/configuration-view.html +18 -18
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +18 -18
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +18 -18
- nautobot/project-static/docs/development/apps/api/models/global-search.html +18 -18
- nautobot/project-static/docs/development/apps/api/models/graphql.html +18 -18
- nautobot/project-static/docs/development/apps/api/models/index.html +33 -22
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +18 -18
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +18 -18
- nautobot/project-static/docs/development/apps/api/prometheus.html +18 -18
- nautobot/project-static/docs/development/apps/api/setup.html +18 -18
- nautobot/project-static/docs/development/apps/api/testing.html +18 -18
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +18 -18
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +18 -18
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +18 -18
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +18 -18
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/base-template.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/index.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/notes.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +18 -18
- nautobot/project-static/docs/development/apps/api/views/urls.html +18 -18
- nautobot/project-static/docs/development/apps/index.html +18 -18
- nautobot/project-static/docs/development/apps/migration/code-updates.html +18 -18
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +18 -18
- nautobot/project-static/docs/development/apps/migration/from-v1.html +18 -18
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +18 -18
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +18 -18
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +18 -18
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +18 -18
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +18 -18
- nautobot/project-static/docs/development/core/application-registry.html +18 -18
- nautobot/project-static/docs/development/core/best-practices.html +18 -18
- nautobot/project-static/docs/development/core/bootstrap-ui.html +18 -18
- nautobot/project-static/docs/development/core/caching.html +18 -18
- nautobot/project-static/docs/development/core/controllers.html +18 -18
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +18 -18
- nautobot/project-static/docs/development/core/generic-views.html +18 -18
- nautobot/project-static/docs/development/core/getting-started.html +18 -18
- nautobot/project-static/docs/development/core/homepage.html +18 -18
- nautobot/project-static/docs/development/core/index.html +29 -18
- nautobot/project-static/docs/development/core/model-checklist.html +26 -20
- nautobot/project-static/docs/development/core/model-features.html +18 -18
- nautobot/project-static/docs/development/core/natural-keys.html +18 -18
- nautobot/project-static/docs/development/core/navigation-menu.html +18 -18
- nautobot/project-static/docs/development/core/release-checklist.html +18 -18
- nautobot/project-static/docs/development/core/role-internals.html +18 -18
- nautobot/project-static/docs/development/core/settings.html +18 -18
- nautobot/project-static/docs/development/core/style-guide.html +19 -19
- nautobot/project-static/docs/development/core/templates.html +18 -18
- nautobot/project-static/docs/development/core/testing.html +18 -18
- nautobot/project-static/docs/development/core/user-preferences.html +18 -18
- nautobot/project-static/docs/development/index.html +18 -18
- nautobot/project-static/docs/development/jobs/index.html +393 -379
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +18 -18
- nautobot/project-static/docs/index.html +9032 -13
- nautobot/project-static/docs/models/extras/metadatachoice.html +3 -3
- nautobot/project-static/docs/models/extras/metadatatype.html +3 -3
- nautobot/project-static/docs/models/extras/objectmetadata.html +3 -3
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +18 -18
- nautobot/project-static/docs/overview/design_philosophy.html +20 -20
- nautobot/project-static/docs/overview/index.html +13 -9032
- nautobot/project-static/docs/release-notes/index.html +252 -19
- nautobot/project-static/docs/release-notes/version-1.0.html +18 -18
- nautobot/project-static/docs/release-notes/version-1.1.html +18 -18
- nautobot/project-static/docs/release-notes/version-1.2.html +18 -18
- nautobot/project-static/docs/release-notes/version-1.3.html +18 -18
- nautobot/project-static/docs/release-notes/version-1.4.html +18 -18
- nautobot/project-static/docs/release-notes/version-1.5.html +18 -18
- nautobot/project-static/docs/release-notes/version-1.6.html +18 -18
- nautobot/project-static/docs/release-notes/version-2.0.html +18 -18
- nautobot/project-static/docs/release-notes/version-2.1.html +18 -18
- nautobot/project-static/docs/release-notes/version-2.2.html +248 -111
- nautobot/project-static/docs/release-notes/version-2.3.html +775 -91
- nautobot/project-static/docs/requirements.txt +3 -3
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +278 -278
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +18 -18
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +18 -18
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +18 -18
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +18 -18
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +55 -23
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +18 -18
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +22 -18
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +18 -18
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +18 -18
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +18 -18
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +18 -18
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +69 -82
- nautobot/project-static/docs/user-guide/administration/installation/index.html +24 -24
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +60 -52
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +80 -87
- nautobot/project-static/docs/user-guide/administration/installation/services.html +37 -44
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +18 -18
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +18 -18
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +18 -18
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +18 -18
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +18 -18
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +76 -24
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +18 -18
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +19 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +19 -19
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +62 -18
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +18 -18
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +18 -18
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +18 -18
- nautobot/project-static/docs/user-guide/index.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +21 -21
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +36 -36
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +33 -33
- nautobot/project-static/docs/user-guide/platform-functionality/{metadata.html → objectmetadata.html} +197 -84
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +21 -21
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +18 -18
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +18 -18
- nautobot/project-static/js/homepage_layout.js +3 -0
- nautobot/tenancy/templates/tenancy/tenant.html +4 -4
- nautobot/virtualization/models.py +0 -2
- nautobot/virtualization/tables.py +2 -5
- {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/METADATA +3 -3
- {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/RECORD +397 -393
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
- {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/NOTICE +0 -0
- {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/WHEEL +0 -0
- {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/entry_points.txt +0 -0
nautobot/extras/models/jobs.py
CHANGED
|
@@ -4,7 +4,6 @@ import contextlib
|
|
|
4
4
|
from datetime import timedelta
|
|
5
5
|
import logging
|
|
6
6
|
|
|
7
|
-
from celery import schedules
|
|
8
7
|
from celery.exceptions import NotRegistered
|
|
9
8
|
from celery.utils.log import get_logger, LoggingProxy
|
|
10
9
|
from django.conf import settings
|
|
@@ -16,7 +15,9 @@ from django.db.models import signals
|
|
|
16
15
|
from django.utils import timezone
|
|
17
16
|
from django.utils.functional import cached_property
|
|
18
17
|
from django_celery_beat.clockedschedule import clocked
|
|
18
|
+
from django_celery_beat.tzcrontab import TzAwareCrontab
|
|
19
19
|
from prometheus_client import Histogram
|
|
20
|
+
from timezone_field import TimeZoneField
|
|
20
21
|
|
|
21
22
|
from nautobot.core.celery import (
|
|
22
23
|
app,
|
|
@@ -456,6 +457,8 @@ class JobLogEntry(BaseModel):
|
|
|
456
457
|
log_object = models.CharField(max_length=JOB_LOG_MAX_LOG_OBJECT_LENGTH, blank=True, default="")
|
|
457
458
|
absolute_url = models.CharField(max_length=JOB_LOG_MAX_ABSOLUTE_URL_LENGTH, blank=True, default="")
|
|
458
459
|
|
|
460
|
+
is_metadata_associable_model = False
|
|
461
|
+
|
|
459
462
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
460
463
|
|
|
461
464
|
def __str__(self):
|
|
@@ -933,6 +936,9 @@ class ScheduledJob(BaseModel):
|
|
|
933
936
|
verbose_name="Start Datetime",
|
|
934
937
|
help_text="Datetime when the schedule should begin triggering the task to run",
|
|
935
938
|
)
|
|
939
|
+
# Django always stores DateTimeField as UTC internally, but we want scheduled jobs to respect DST and similar,
|
|
940
|
+
# so we need to store the time zone the job was scheduled under as well.
|
|
941
|
+
time_zone = TimeZoneField(default=timezone.get_default_timezone_name)
|
|
936
942
|
# todoindex:
|
|
937
943
|
enabled = models.BooleanField(
|
|
938
944
|
default=True,
|
|
@@ -1003,12 +1009,15 @@ class ScheduledJob(BaseModel):
|
|
|
1003
1009
|
def __str__(self):
|
|
1004
1010
|
return f"{self.name}: {self.interval}"
|
|
1005
1011
|
|
|
1012
|
+
class Meta:
|
|
1013
|
+
ordering = ["name"]
|
|
1014
|
+
|
|
1006
1015
|
def save(self, *args, **kwargs):
|
|
1007
1016
|
self.queue = self.queue or ""
|
|
1008
1017
|
# make sure non-valid crontab doesn't get saved
|
|
1009
1018
|
if self.interval == JobExecutionType.TYPE_CUSTOM:
|
|
1010
1019
|
try:
|
|
1011
|
-
self.get_crontab(self.crontab)
|
|
1020
|
+
self.get_crontab(self.crontab, tz=self.time_zone)
|
|
1012
1021
|
except Exception as e:
|
|
1013
1022
|
raise ValidationError({"crontab": e})
|
|
1014
1023
|
if not self.enabled:
|
|
@@ -1053,7 +1062,7 @@ class ScheduledJob(BaseModel):
|
|
|
1053
1062
|
return timezone.now() + timedelta(seconds=15)
|
|
1054
1063
|
|
|
1055
1064
|
@classmethod
|
|
1056
|
-
def get_crontab(cls, crontab):
|
|
1065
|
+
def get_crontab(cls, crontab, tz=None):
|
|
1057
1066
|
"""
|
|
1058
1067
|
Wrapper method translates crontab syntax to Celery crontab.
|
|
1059
1068
|
|
|
@@ -1066,25 +1075,112 @@ class ScheduledJob(BaseModel):
|
|
|
1066
1075
|
|
|
1067
1076
|
No support for Last (L), Weekday (W), Number symbol (#), Question mark (?), and special @ strings.
|
|
1068
1077
|
"""
|
|
1078
|
+
if not tz:
|
|
1079
|
+
tz = timezone.get_default_timezone()
|
|
1069
1080
|
minute, hour, day_of_month, month_of_year, day_of_week = crontab.split(" ")
|
|
1070
|
-
|
|
1081
|
+
|
|
1082
|
+
return TzAwareCrontab(
|
|
1071
1083
|
minute=minute,
|
|
1072
1084
|
hour=hour,
|
|
1073
1085
|
day_of_month=day_of_month,
|
|
1074
1086
|
month_of_year=month_of_year,
|
|
1075
1087
|
day_of_week=day_of_week,
|
|
1088
|
+
tz=tz,
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
@classmethod
|
|
1092
|
+
def create_schedule(
|
|
1093
|
+
cls,
|
|
1094
|
+
job_model,
|
|
1095
|
+
user,
|
|
1096
|
+
name=None,
|
|
1097
|
+
start_time=None,
|
|
1098
|
+
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1099
|
+
crontab="",
|
|
1100
|
+
profile=False,
|
|
1101
|
+
approval_required=False,
|
|
1102
|
+
task_queue=None,
|
|
1103
|
+
**job_kwargs,
|
|
1104
|
+
):
|
|
1105
|
+
"""
|
|
1106
|
+
Schedule a job with the specified parameters.
|
|
1107
|
+
|
|
1108
|
+
This method creates a schedule for a job to be executed at a specific time
|
|
1109
|
+
or interval. It handles immediate execution, custom start times, and
|
|
1110
|
+
crontab-based scheduling.
|
|
1111
|
+
|
|
1112
|
+
Parameters:
|
|
1113
|
+
job_model (JobModel): The job model instance.
|
|
1114
|
+
user (User): The user who is scheduling the job.
|
|
1115
|
+
name (str, optional): The name of the scheduled job. Defaults to None.
|
|
1116
|
+
start_time (datetime, optional): The start time for the job. Defaults to None.
|
|
1117
|
+
interval (JobExecutionType, optional): The interval type for the job execution.
|
|
1118
|
+
Defaults to JobExecutionType.TYPE_IMMEDIATELY.
|
|
1119
|
+
crontab (str, optional): The crontab string for the schedule. Defaults to "".
|
|
1120
|
+
profile (bool, optional): Flag indicating whether to profile the job. Defaults to False.
|
|
1121
|
+
approval_required (bool, optional): Flag indicating if approval is required. Defaults to False.
|
|
1122
|
+
task_queue (str, optional): The task queue for the job. Defaults to None, which will use the configured default celery queue.
|
|
1123
|
+
**job_kwargs: Additional keyword arguments to pass to the job.
|
|
1124
|
+
|
|
1125
|
+
Returns:
|
|
1126
|
+
ScheduledJob instance
|
|
1127
|
+
"""
|
|
1128
|
+
|
|
1129
|
+
if interval == JobExecutionType.TYPE_IMMEDIATELY:
|
|
1130
|
+
start_time = timezone.localtime()
|
|
1131
|
+
name = name or f"{job_model.name} - {start_time}"
|
|
1132
|
+
elif interval == JobExecutionType.TYPE_CUSTOM:
|
|
1133
|
+
if start_time is None:
|
|
1134
|
+
# "start_time" is checked against models.ScheduledJob.earliest_possible_time()
|
|
1135
|
+
# which returns timezone.now() + timedelta(seconds=15)
|
|
1136
|
+
start_time = timezone.localtime() + timedelta(seconds=20)
|
|
1137
|
+
|
|
1138
|
+
celery_kwargs = {
|
|
1139
|
+
"nautobot_job_profile": profile,
|
|
1140
|
+
"queue": task_queue,
|
|
1141
|
+
}
|
|
1142
|
+
if job_model.soft_time_limit > 0:
|
|
1143
|
+
celery_kwargs["soft_time_limit"] = job_model.soft_time_limit
|
|
1144
|
+
if job_model.time_limit > 0:
|
|
1145
|
+
celery_kwargs["time_limit"] = job_model.time_limit
|
|
1146
|
+
|
|
1147
|
+
# 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
|
|
1148
|
+
#
|
|
1149
|
+
# We pass in task and job_model here partly for forward/backward compatibility logic, and
|
|
1150
|
+
# part fallback safety. It's mildly useful to store both the task module/class name and the JobModel
|
|
1151
|
+
# FK on the ScheduledJob, as in the case where the JobModel gets deleted (and the FK becomes
|
|
1152
|
+
# null) you still have a bit of context on the ScheduledJob as to what it was originally
|
|
1153
|
+
# scheduled for.
|
|
1154
|
+
scheduled_job = cls(
|
|
1155
|
+
name=name,
|
|
1156
|
+
task=job_model.class_path,
|
|
1157
|
+
job_model=job_model,
|
|
1158
|
+
start_time=start_time,
|
|
1159
|
+
time_zone=start_time.tzinfo,
|
|
1160
|
+
description=f"Nautobot job {name} scheduled by {user} for {start_time}",
|
|
1161
|
+
kwargs=job_kwargs,
|
|
1162
|
+
celery_kwargs=celery_kwargs,
|
|
1163
|
+
interval=interval,
|
|
1164
|
+
one_off=(interval == JobExecutionType.TYPE_FUTURE),
|
|
1165
|
+
user=user,
|
|
1166
|
+
approval_required=approval_required,
|
|
1167
|
+
crontab=crontab,
|
|
1168
|
+
queue=task_queue,
|
|
1076
1169
|
)
|
|
1170
|
+
scheduled_job.validated_save()
|
|
1171
|
+
return scheduled_job
|
|
1077
1172
|
|
|
1078
1173
|
def to_cron(self):
|
|
1079
|
-
|
|
1174
|
+
tz = self.time_zone
|
|
1175
|
+
t = self.start_time.astimezone(tz)
|
|
1080
1176
|
if self.interval == JobExecutionType.TYPE_HOURLY:
|
|
1081
|
-
return
|
|
1177
|
+
return TzAwareCrontab(minute=t.minute, tz=tz)
|
|
1082
1178
|
elif self.interval == JobExecutionType.TYPE_DAILY:
|
|
1083
|
-
return
|
|
1179
|
+
return TzAwareCrontab(minute=t.minute, hour=t.hour, tz=tz)
|
|
1084
1180
|
elif self.interval == JobExecutionType.TYPE_WEEKLY:
|
|
1085
|
-
return
|
|
1181
|
+
return TzAwareCrontab(minute=t.minute, hour=t.hour, day_of_week=t.strftime("%w"), tz=tz)
|
|
1086
1182
|
elif self.interval == JobExecutionType.TYPE_CUSTOM:
|
|
1087
|
-
return self.get_crontab(self.crontab)
|
|
1183
|
+
return self.get_crontab(self.crontab, tz=tz)
|
|
1088
1184
|
raise ValueError(f"I do not know to convert {self.interval} to a Cronjob!")
|
|
1089
1185
|
|
|
1090
1186
|
|
|
@@ -61,7 +61,7 @@ class MetadataType(PrimaryModel):
|
|
|
61
61
|
|
|
62
62
|
objects = MetadataTypeManager()
|
|
63
63
|
clone_fields = ["data_type"]
|
|
64
|
-
documentation_static_path = "docs/user-guide/platform-functionality/
|
|
64
|
+
documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
|
|
65
65
|
|
|
66
66
|
class Meta:
|
|
67
67
|
ordering = ["name"]
|
|
@@ -106,7 +106,7 @@ class MetadataChoice(ChangeLoggedModel, BaseModel):
|
|
|
106
106
|
weight = models.PositiveSmallIntegerField(default=100, help_text="Higher weights appear later in the list")
|
|
107
107
|
is_metadata_associable_model = False
|
|
108
108
|
|
|
109
|
-
documentation_static_path = "docs/user-guide/platform-functionality/
|
|
109
|
+
documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
|
|
110
110
|
|
|
111
111
|
class Meta:
|
|
112
112
|
ordering = ["metadata_type", "weight", "value"]
|
|
@@ -166,19 +166,19 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
166
166
|
metadata_type = models.ForeignKey(
|
|
167
167
|
to=MetadataType,
|
|
168
168
|
on_delete=models.PROTECT,
|
|
169
|
-
related_name="
|
|
169
|
+
related_name="object_metadata",
|
|
170
170
|
)
|
|
171
171
|
contact = models.ForeignKey(
|
|
172
172
|
to=Contact,
|
|
173
173
|
on_delete=models.PROTECT,
|
|
174
|
-
related_name="
|
|
174
|
+
related_name="object_metadata",
|
|
175
175
|
blank=True,
|
|
176
176
|
null=True,
|
|
177
177
|
)
|
|
178
178
|
team = models.ForeignKey(
|
|
179
179
|
to=Team,
|
|
180
180
|
on_delete=models.PROTECT,
|
|
181
|
-
related_name="
|
|
181
|
+
related_name="object_metadata",
|
|
182
182
|
blank=True,
|
|
183
183
|
null=True,
|
|
184
184
|
)
|
|
@@ -186,6 +186,7 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
186
186
|
base_field=models.CharField(
|
|
187
187
|
max_length=CHARFIELD_MAX_LENGTH,
|
|
188
188
|
),
|
|
189
|
+
blank=True,
|
|
189
190
|
help_text="List of scoped fields, only direct fields on the model",
|
|
190
191
|
)
|
|
191
192
|
_value = models.JSONField(
|
|
@@ -199,7 +200,7 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
199
200
|
|
|
200
201
|
objects = ObjectMetadataManager()
|
|
201
202
|
natural_key_field_names = ["pk"]
|
|
202
|
-
documentation_static_path = "docs/user-guide/platform-functionality/
|
|
203
|
+
documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
|
|
203
204
|
|
|
204
205
|
class Meta:
|
|
205
206
|
ordering = ["metadata_type"]
|
|
@@ -361,12 +362,17 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
361
362
|
raise ValidationError("Date values must be in the format YYYY-MM-DD.")
|
|
362
363
|
# Validate datetime
|
|
363
364
|
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_DATETIME:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
365
|
+
if isinstance(value, datetime):
|
|
366
|
+
# check if datetime object has tzinfo
|
|
367
|
+
if value.tzinfo is None:
|
|
368
|
+
value = value.replace(tzinfo=timezone.utc) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
|
369
|
+
value = value.replace(microsecond=0).isoformat() # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
|
370
|
+
else:
|
|
371
|
+
acceptable_datetime_formats = [
|
|
372
|
+
"YYYY-MM-DDTHH:MM:SS",
|
|
373
|
+
"YYYY-MM-DDTHH:MM:SS(+,-)zzzz",
|
|
374
|
+
"YYYY-MM-DDTHH:MM:SS(+,-)zz:zz",
|
|
375
|
+
]
|
|
370
376
|
try:
|
|
371
377
|
datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z")
|
|
372
378
|
except ValueError:
|
|
@@ -380,12 +386,6 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
380
386
|
)
|
|
381
387
|
except TypeError:
|
|
382
388
|
raise ValidationError("Value must be a datetime or str object.")
|
|
383
|
-
else:
|
|
384
|
-
# if value is a datetime object
|
|
385
|
-
# check if datetime object has tzinfo
|
|
386
|
-
if value.tzinfo is None:
|
|
387
|
-
value = value.replace(tzinfo=timezone.utc)
|
|
388
|
-
value = value.replace(microsecond=0).isoformat()
|
|
389
389
|
# Validate selected choice
|
|
390
390
|
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_SELECT:
|
|
391
391
|
if value not in self.metadata_type.choices.values_list("value", flat=True):
|
nautobot/extras/models/models.py
CHANGED
|
@@ -822,6 +822,8 @@ class Note(ChangeLoggedModel, BaseModel):
|
|
|
822
822
|
note = models.TextField()
|
|
823
823
|
objects = BaseManager.from_queryset(NotesQuerySet)()
|
|
824
824
|
|
|
825
|
+
is_metadata_associable_model = False
|
|
826
|
+
|
|
825
827
|
class Meta:
|
|
826
828
|
ordering = ["created"]
|
|
827
829
|
unique_together = [["assigned_object_type", "assigned_object_id", "user_name", "created"]]
|
nautobot/extras/signals.py
CHANGED
|
@@ -13,7 +13,7 @@ from django.core.cache import cache
|
|
|
13
13
|
from django.core.exceptions import ValidationError
|
|
14
14
|
from django.core.files.storage import get_storage_class
|
|
15
15
|
from django.db import transaction
|
|
16
|
-
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete, pre_save
|
|
16
|
+
from django.db.models.signals import m2m_changed, post_delete, post_migrate, post_save, pre_delete, pre_save
|
|
17
17
|
from django.dispatch import receiver
|
|
18
18
|
from django.utils import timezone
|
|
19
19
|
from django_prometheus.models import model_deletes, model_inserts, model_updates
|
|
@@ -289,6 +289,19 @@ def _handle_deleted_object(sender, instance, **kwargs):
|
|
|
289
289
|
model_deletes.labels(instance._meta.model_name).inc()
|
|
290
290
|
|
|
291
291
|
|
|
292
|
+
#
|
|
293
|
+
# Content types
|
|
294
|
+
#
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@receiver(post_migrate)
|
|
298
|
+
def post_migrate_clear_content_type_caches(sender, app_config, signal, **kwargs):
|
|
299
|
+
"""Clear various content-type caches after a migration."""
|
|
300
|
+
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
301
|
+
cache.delete("nautobot.extras.utils.change_logged_models_queryset")
|
|
302
|
+
cache.delete_pattern("nautobot.extras.utils.FeatureQuery.*")
|
|
303
|
+
|
|
304
|
+
|
|
292
305
|
#
|
|
293
306
|
# Custom fields
|
|
294
307
|
#
|
nautobot/extras/tables.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
|
-
from django.utils.html import format_html
|
|
2
|
+
from django.utils.html import format_html, format_html_join
|
|
3
3
|
import django_tables2 as tables
|
|
4
4
|
from django_tables2.utils import Accessor
|
|
5
5
|
from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
|
|
@@ -107,6 +107,11 @@ GITREPOSITORY_BUTTONS = """
|
|
|
107
107
|
|
|
108
108
|
JOB_BUTTONS = """
|
|
109
109
|
<a href="{% url 'extras:job' pk=record.pk %}" class="btn btn-default btn-xs" title="Details"><i class="mdi mdi-information-outline" aria-hidden="true"></i></a>
|
|
110
|
+
<a href="{% url 'extras:jobresult_list' %}?job_model={{ record.name | urlencode }}" class="btn btn-default btn-xs" title="Job Results"><i class="mdi mdi-format-list-bulleted" aria-hidden="true"></i></a>
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
SCHEDULED_JOB_BUTTONS = """
|
|
114
|
+
<a href="{% url 'extras:jobresult_list' %}?scheduled_job={{ record.name | urlencode }}" class="btn btn-default btn-xs" title="Job Results"><i class="mdi mdi-format-list-bulleted" aria-hidden="true"></i></a>
|
|
110
115
|
"""
|
|
111
116
|
|
|
112
117
|
OBJECTCHANGE_OBJECT = """
|
|
@@ -715,7 +720,10 @@ class JobTable(BaseTable):
|
|
|
715
720
|
return render_markdown(value)
|
|
716
721
|
|
|
717
722
|
def render_name(self, value):
|
|
718
|
-
return format_html(
|
|
723
|
+
return format_html(
|
|
724
|
+
'<span class="btn btn-primary btn-xs"><i class="mdi mdi-play"></i></span>{}',
|
|
725
|
+
value,
|
|
726
|
+
)
|
|
719
727
|
|
|
720
728
|
class Meta(BaseTable.Meta):
|
|
721
729
|
model = JobModel
|
|
@@ -831,6 +839,10 @@ class JobResultTable(BaseTable):
|
|
|
831
839
|
orderable=False,
|
|
832
840
|
attrs={"td": {"class": "text-nowrap report-stats"}},
|
|
833
841
|
)
|
|
842
|
+
scheduled_job = tables.Column(
|
|
843
|
+
linkify=True,
|
|
844
|
+
verbose_name="Scheduled Job",
|
|
845
|
+
)
|
|
834
846
|
actions = tables.TemplateColumn(
|
|
835
847
|
template_code="""
|
|
836
848
|
{% load helpers %}
|
|
@@ -880,6 +892,7 @@ class JobResultTable(BaseTable):
|
|
|
880
892
|
"date_created",
|
|
881
893
|
"name",
|
|
882
894
|
"job_model",
|
|
895
|
+
"scheduled_job",
|
|
883
896
|
"duration",
|
|
884
897
|
"date_done",
|
|
885
898
|
"user",
|
|
@@ -887,7 +900,16 @@ class JobResultTable(BaseTable):
|
|
|
887
900
|
"summary",
|
|
888
901
|
"actions",
|
|
889
902
|
)
|
|
890
|
-
default_columns = (
|
|
903
|
+
default_columns = (
|
|
904
|
+
"pk",
|
|
905
|
+
"date_created",
|
|
906
|
+
"name",
|
|
907
|
+
"job_model",
|
|
908
|
+
"user",
|
|
909
|
+
"status",
|
|
910
|
+
"summary",
|
|
911
|
+
"actions",
|
|
912
|
+
)
|
|
891
913
|
|
|
892
914
|
|
|
893
915
|
class JobButtonTable(BaseTable):
|
|
@@ -957,10 +979,8 @@ class MetadataTypeTable(BaseTable):
|
|
|
957
979
|
class ObjectMetadataTable(BaseTable):
|
|
958
980
|
pk = ToggleColumn()
|
|
959
981
|
metadata_type = tables.Column(linkify=True)
|
|
960
|
-
assigned_object = tables.TemplateColumn(
|
|
961
|
-
|
|
962
|
-
ObjectMetadata,
|
|
963
|
-
buttons=("delete"),
|
|
982
|
+
assigned_object = tables.TemplateColumn(
|
|
983
|
+
template_code=ASSIGNED_OBJECT, verbose_name="Assigned object", orderable=False
|
|
964
984
|
)
|
|
965
985
|
# This is needed so that render_value method below does not skip itself
|
|
966
986
|
# when metadata_type.data_type is TYPE_CONTACT_TEAM and we need it to display either contact or team
|
|
@@ -974,7 +994,6 @@ class ObjectMetadataTable(BaseTable):
|
|
|
974
994
|
"metadata_type",
|
|
975
995
|
"scoped_fields",
|
|
976
996
|
"value",
|
|
977
|
-
"actions",
|
|
978
997
|
)
|
|
979
998
|
default_columns = (
|
|
980
999
|
"pk",
|
|
@@ -982,11 +1001,12 @@ class ObjectMetadataTable(BaseTable):
|
|
|
982
1001
|
"scoped_fields",
|
|
983
1002
|
"value",
|
|
984
1003
|
"metadata_type",
|
|
985
|
-
"actions",
|
|
986
1004
|
)
|
|
987
1005
|
|
|
988
|
-
def render_scoped_fields(self,
|
|
989
|
-
|
|
1006
|
+
def render_scoped_fields(self, value):
|
|
1007
|
+
if not value:
|
|
1008
|
+
return "(all fields)"
|
|
1009
|
+
return format_html_join(", ", "<code>{}</code>", ([v] for v in sorted(value)))
|
|
990
1010
|
|
|
991
1011
|
def render_value(self, record):
|
|
992
1012
|
if record.value is not None and record.metadata_type.data_type == MetadataTypeDataTypeChoices.TYPE_JSON:
|
|
@@ -1027,16 +1047,37 @@ class NoteTable(BaseTable):
|
|
|
1027
1047
|
|
|
1028
1048
|
class ScheduledJobTable(BaseTable):
|
|
1029
1049
|
pk = ToggleColumn()
|
|
1030
|
-
name = tables.
|
|
1050
|
+
name = tables.Column(linkify=True)
|
|
1031
1051
|
job_model = tables.Column(verbose_name="Job", linkify=True)
|
|
1032
1052
|
interval = tables.Column(verbose_name="Execution Type")
|
|
1033
|
-
start_time = tables.
|
|
1034
|
-
last_run_at = tables.
|
|
1053
|
+
start_time = tables.DateTimeColumn(verbose_name="First Run", format=settings.SHORT_DATETIME_FORMAT)
|
|
1054
|
+
last_run_at = tables.DateTimeColumn(verbose_name="Most Recent Run", format=settings.SHORT_DATETIME_FORMAT)
|
|
1055
|
+
crontab = tables.Column()
|
|
1035
1056
|
total_run_count = tables.Column(verbose_name="Total Run Count")
|
|
1057
|
+
actions = ButtonsColumn(ScheduledJob, buttons=("delete"), prepend_template=SCHEDULED_JOB_BUTTONS)
|
|
1036
1058
|
|
|
1037
1059
|
class Meta(BaseTable.Meta):
|
|
1038
1060
|
model = ScheduledJob
|
|
1039
|
-
fields = (
|
|
1061
|
+
fields = (
|
|
1062
|
+
"pk",
|
|
1063
|
+
"name",
|
|
1064
|
+
"total_run_count",
|
|
1065
|
+
"job_model",
|
|
1066
|
+
"interval",
|
|
1067
|
+
"start_time",
|
|
1068
|
+
"last_run_at",
|
|
1069
|
+
"crontab",
|
|
1070
|
+
"time_zone",
|
|
1071
|
+
"actions",
|
|
1072
|
+
)
|
|
1073
|
+
default_columns = (
|
|
1074
|
+
"pk",
|
|
1075
|
+
"name",
|
|
1076
|
+
"job_model",
|
|
1077
|
+
"interval",
|
|
1078
|
+
"last_run_at",
|
|
1079
|
+
"actions",
|
|
1080
|
+
)
|
|
1040
1081
|
|
|
1041
1082
|
|
|
1042
1083
|
class ScheduledJobApprovalQueueTable(BaseTable):
|
|
@@ -1108,7 +1149,15 @@ class RelationshipAssociationTable(BaseTable):
|
|
|
1108
1149
|
|
|
1109
1150
|
class Meta(BaseTable.Meta):
|
|
1110
1151
|
model = RelationshipAssociation
|
|
1111
|
-
fields = (
|
|
1152
|
+
fields = (
|
|
1153
|
+
"pk",
|
|
1154
|
+
"relationship",
|
|
1155
|
+
"source_type",
|
|
1156
|
+
"source",
|
|
1157
|
+
"destination_type",
|
|
1158
|
+
"destination",
|
|
1159
|
+
"actions",
|
|
1160
|
+
)
|
|
1112
1161
|
default_columns = ("pk", "relationship", "source", "destination", "actions")
|
|
1113
1162
|
|
|
1114
1163
|
|
|
@@ -1224,7 +1273,15 @@ class TagTable(BaseTable):
|
|
|
1224
1273
|
|
|
1225
1274
|
class Meta(BaseTable.Meta):
|
|
1226
1275
|
model = Tag
|
|
1227
|
-
fields = (
|
|
1276
|
+
fields = (
|
|
1277
|
+
"pk",
|
|
1278
|
+
"name",
|
|
1279
|
+
"items",
|
|
1280
|
+
"color",
|
|
1281
|
+
"content_types",
|
|
1282
|
+
"description",
|
|
1283
|
+
"actions",
|
|
1284
|
+
)
|
|
1228
1285
|
|
|
1229
1286
|
|
|
1230
1287
|
class TaggedItemTable(BaseTable):
|
|
@@ -1304,7 +1361,9 @@ class WebhookTable(BaseTable):
|
|
|
1304
1361
|
class AssociatedContactsTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
1305
1362
|
pk = ToggleColumn()
|
|
1306
1363
|
contact_type = tables.TemplateColumn(
|
|
1307
|
-
CONTACT_OR_TEAM_ICON,
|
|
1364
|
+
CONTACT_OR_TEAM_ICON,
|
|
1365
|
+
verbose_name="Type",
|
|
1366
|
+
attrs={"td": {"style": "width:20px;"}},
|
|
1308
1367
|
)
|
|
1309
1368
|
name = tables.TemplateColumn(CONTACT_OR_TEAM, verbose_name="Name")
|
|
1310
1369
|
contact_or_team_phone = tables.TemplateColumn(PHONE, accessor="contact_or_team.phone", verbose_name="Phone")
|
|
@@ -115,6 +115,17 @@
|
|
|
115
115
|
<td>{{ object.enabled | render_boolean }}</td>
|
|
116
116
|
<td></td>
|
|
117
117
|
</tr>
|
|
118
|
+
<tr>
|
|
119
|
+
<td>Job Results</td>
|
|
120
|
+
<td>
|
|
121
|
+
{% if object.job_results.exists %}
|
|
122
|
+
<a href="{% url 'extras:jobresult_list' %}?job_model={{ object.name | urlencode }}">{{ object.job_results.count }}</a>
|
|
123
|
+
{% else %}
|
|
124
|
+
{{ None|placeholder }}
|
|
125
|
+
{% endif %}
|
|
126
|
+
</td>
|
|
127
|
+
<td></td>
|
|
128
|
+
</tr>
|
|
118
129
|
</table>
|
|
119
130
|
</div>
|
|
120
131
|
{% endblock content_left_page %}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
{% load helpers %}
|
|
5
5
|
{% load perms %}
|
|
6
6
|
{% load plugins %}
|
|
7
|
+
{% load tz %}
|
|
7
8
|
|
|
8
9
|
{% block buttons %}
|
|
9
10
|
{% plugin_buttons object %}
|
|
@@ -94,13 +95,23 @@
|
|
|
94
95
|
<tr>
|
|
95
96
|
<td>Start Time</td>
|
|
96
97
|
<td>
|
|
97
|
-
{{ object.start_time }}
|
|
98
|
+
{{ object.start_time|timezone:object.time_zone|date:'Y-m-d H:i:s T' }}
|
|
99
|
+
{% if default_time_zone != object.time_zone %}
|
|
100
|
+
<br>{{ object.start_time|timezone:default_time_zone|date:'Y-m-d H:i:s T' }}
|
|
101
|
+
{% endif %}
|
|
98
102
|
</td>
|
|
99
103
|
</tr>
|
|
100
104
|
<tr>
|
|
101
105
|
<td>Last Run At</td>
|
|
102
106
|
<td>
|
|
103
|
-
{
|
|
107
|
+
{% if object.last_run_at %}
|
|
108
|
+
{{ object.last_run_at|timezone:object.time_zone|date:'Y-m-d H:i:s T' }}
|
|
109
|
+
{% if default_time_zone != object.time_zone %}
|
|
110
|
+
<br>{{ object.last_run_at|timezone:default_time_zone|date:'Y-m-d H:i:s T' }}
|
|
111
|
+
{% endif %}
|
|
112
|
+
{% else %}
|
|
113
|
+
{{ object.last_run_at|placeholder }}
|
|
114
|
+
{% endif %}
|
|
104
115
|
</td>
|
|
105
116
|
</tr>
|
|
106
117
|
<tr>
|