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/api/views.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from datetime import timedelta
|
|
2
|
-
|
|
3
1
|
from django.conf import settings
|
|
4
2
|
from django.contrib.contenttypes.models import ContentType
|
|
5
3
|
from django.forms import ValidationError as FormsValidationError
|
|
@@ -492,59 +490,6 @@ class ImageAttachmentViewSet(ModelViewSet):
|
|
|
492
490
|
#
|
|
493
491
|
|
|
494
492
|
|
|
495
|
-
def _create_schedule(serializer, data, job_model, user, approval_required, task_queue=None):
|
|
496
|
-
"""
|
|
497
|
-
This is an internal function to create a scheduled job from API data.
|
|
498
|
-
It has to handle both once-offs (i.e. of type TYPE_FUTURE) and interval
|
|
499
|
-
jobs.
|
|
500
|
-
"""
|
|
501
|
-
type_ = serializer["interval"]
|
|
502
|
-
if type_ == JobExecutionType.TYPE_IMMEDIATELY:
|
|
503
|
-
time = timezone.now()
|
|
504
|
-
name = serializer.get("name") or f"{job_model.name} - {time}"
|
|
505
|
-
elif type_ == JobExecutionType.TYPE_CUSTOM:
|
|
506
|
-
time = serializer.get("start_time") # doing .get("key", "default") returns None instead of "default"
|
|
507
|
-
if time is None:
|
|
508
|
-
# "start_time" is checked against models.ScheduledJob.earliest_possible_time()
|
|
509
|
-
# which returns timezone.now() + timedelta(seconds=15)
|
|
510
|
-
time = timezone.now() + timedelta(seconds=20)
|
|
511
|
-
name = serializer["name"]
|
|
512
|
-
else:
|
|
513
|
-
time = serializer["start_time"]
|
|
514
|
-
name = serializer["name"]
|
|
515
|
-
crontab = serializer.get("crontab", "")
|
|
516
|
-
|
|
517
|
-
celery_kwargs = {
|
|
518
|
-
"nautobot_job_profile": False,
|
|
519
|
-
"queue": task_queue,
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
# 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
|
|
523
|
-
#
|
|
524
|
-
# We pass in task and job_model here partly for forward/backward compatibility logic, and
|
|
525
|
-
# part fallback safety. It's mildly useful to store both the task module/class name and the JobModel
|
|
526
|
-
# FK on the ScheduledJob, as in the case where the JobModel gets deleted (and the FK becomes
|
|
527
|
-
# null) you still have a bit of context on the ScheduledJob as to what it was originally
|
|
528
|
-
# scheduled for.
|
|
529
|
-
scheduled_job = ScheduledJob(
|
|
530
|
-
name=name,
|
|
531
|
-
task=job_model.class_path,
|
|
532
|
-
job_model=job_model,
|
|
533
|
-
start_time=time,
|
|
534
|
-
description=f"Nautobot job {name} scheduled by {user} for {time}",
|
|
535
|
-
kwargs=data,
|
|
536
|
-
celery_kwargs=celery_kwargs,
|
|
537
|
-
interval=type_,
|
|
538
|
-
one_off=(type_ == JobExecutionType.TYPE_FUTURE),
|
|
539
|
-
user=user,
|
|
540
|
-
approval_required=approval_required,
|
|
541
|
-
crontab=crontab,
|
|
542
|
-
queue=task_queue,
|
|
543
|
-
)
|
|
544
|
-
scheduled_job.validated_save()
|
|
545
|
-
return scheduled_job
|
|
546
|
-
|
|
547
|
-
|
|
548
493
|
class JobViewSetBase(
|
|
549
494
|
NautobotAPIVersionMixin,
|
|
550
495
|
# note no CreateModelMixin
|
|
@@ -744,13 +689,16 @@ class JobViewSetBase(
|
|
|
744
689
|
|
|
745
690
|
# Try to create a ScheduledJob, or...
|
|
746
691
|
if schedule_data:
|
|
747
|
-
schedule =
|
|
748
|
-
schedule_data,
|
|
749
|
-
job_class.serialize_data(cleaned_data),
|
|
692
|
+
schedule = ScheduledJob.create_schedule(
|
|
750
693
|
job_model,
|
|
751
694
|
request.user,
|
|
752
|
-
|
|
695
|
+
name=schedule_data.get("name"),
|
|
696
|
+
start_time=schedule_data.get("start_time"),
|
|
697
|
+
interval=schedule_data.get("interval"),
|
|
698
|
+
crontab=schedule_data.get("crontab", ""),
|
|
699
|
+
approval_required=approval_required,
|
|
753
700
|
task_queue=input_serializer.validated_data.get("task_queue", None),
|
|
701
|
+
**job_class.serialize_data(cleaned_data),
|
|
754
702
|
)
|
|
755
703
|
else:
|
|
756
704
|
schedule = None
|
nautobot/extras/factory.py
CHANGED
|
@@ -251,6 +251,24 @@ class MetadataTypeFactory(PrimaryModelFactory):
|
|
|
251
251
|
)
|
|
252
252
|
|
|
253
253
|
|
|
254
|
+
def _available_field_names(metadata_type, assigned_object):
|
|
255
|
+
field_names = [field.name for field in assigned_object._meta.get_fields()]
|
|
256
|
+
# Avoid collisions, see ObjectMetadata.clean()
|
|
257
|
+
existing_metadata_scoped_fields = ObjectMetadata.objects.filter(
|
|
258
|
+
metadata_type=metadata_type,
|
|
259
|
+
assigned_object_type=ContentType.objects.get_for_model(assigned_object),
|
|
260
|
+
assigned_object_id=assigned_object.pk,
|
|
261
|
+
).values_list("scoped_fields", flat=True)
|
|
262
|
+
for existing_scoped_fields in existing_metadata_scoped_fields:
|
|
263
|
+
if existing_scoped_fields:
|
|
264
|
+
field_names = sorted(set(field_names).difference(existing_scoped_fields))
|
|
265
|
+
else:
|
|
266
|
+
field_names = []
|
|
267
|
+
break
|
|
268
|
+
|
|
269
|
+
return field_names
|
|
270
|
+
|
|
271
|
+
|
|
254
272
|
class ObjectMetadataFactory(BaseModelFactory):
|
|
255
273
|
"""ObjectMetadata model factory"""
|
|
256
274
|
|
|
@@ -263,7 +281,6 @@ class ObjectMetadataFactory(BaseModelFactory):
|
|
|
263
281
|
MetadataType.objects.all(),
|
|
264
282
|
allow_null=False,
|
|
265
283
|
)
|
|
266
|
-
scoped_fields = factory.Faker("pylist", allowed_types=[str])
|
|
267
284
|
|
|
268
285
|
@factory.lazy_attribute
|
|
269
286
|
def contact(self):
|
|
@@ -308,22 +325,43 @@ class ObjectMetadataFactory(BaseModelFactory):
|
|
|
308
325
|
raise RuntimeError(f"Unsupported metadatatype datatype {metadata_type_data_type}")
|
|
309
326
|
|
|
310
327
|
@factory.lazy_attribute
|
|
311
|
-
def
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
content_type = factory.random.randgen.choice(
|
|
315
|
-
ContentType.objects.filter(FeatureQuery("metadata").get_query(), pk__in=allowed_content_types)
|
|
316
|
-
)
|
|
328
|
+
def assigned_object(self):
|
|
329
|
+
allowed_content_types = list(self.metadata_type.content_types.all())
|
|
330
|
+
for content_type in factory.random.randgen.sample(allowed_content_types, len(allowed_content_types)):
|
|
317
331
|
# It does not have a get_absolute_url attribute and is causing failure in API unittests
|
|
318
332
|
if content_type.app_label == "extras" and content_type.model == "taggeditem":
|
|
319
333
|
continue
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
|
|
335
|
+
assigned_model = content_type.model_class()
|
|
336
|
+
queryset = assigned_model.objects.all()
|
|
337
|
+
|
|
338
|
+
if not queryset.exists():
|
|
339
|
+
continue
|
|
340
|
+
|
|
341
|
+
for _ in range(10):
|
|
342
|
+
assigned_object = factory.random.randgen.choice(queryset)
|
|
343
|
+
if _available_field_names(self.metadata_type, assigned_object):
|
|
344
|
+
return assigned_object
|
|
345
|
+
|
|
346
|
+
raise RuntimeError(f"Couldn't find any suitable instances not already covered by {self.metadata_type}")
|
|
322
347
|
|
|
323
348
|
@factory.lazy_attribute
|
|
324
|
-
def
|
|
325
|
-
|
|
326
|
-
|
|
349
|
+
def scoped_fields(self):
|
|
350
|
+
all_field_names = [field.name for field in self.assigned_object._meta.get_fields()]
|
|
351
|
+
field_names = _available_field_names(self.metadata_type, self.assigned_object)
|
|
352
|
+
if not field_names:
|
|
353
|
+
raise RuntimeError(
|
|
354
|
+
f"All existing scoped_fields for {self.metadata_type} are covered by existing ObjectMetadata for {self.assigned_object}"
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
if len(field_names) < len(all_field_names):
|
|
358
|
+
minimum_fields = 1 # don't allow an empty list since that would cover all fields
|
|
359
|
+
else:
|
|
360
|
+
minimum_fields = 0
|
|
361
|
+
|
|
362
|
+
return factory.random.randgen.sample(
|
|
363
|
+
field_names, k=factory.random.randgen.randint(minimum_fields, len(field_names))
|
|
364
|
+
)
|
|
327
365
|
|
|
328
366
|
|
|
329
367
|
class ObjectChangeFactory(BaseModelFactory):
|
|
@@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
6
6
|
from django.db.models import Q
|
|
7
7
|
import django_filters
|
|
8
8
|
from drf_spectacular.utils import extend_schema_field
|
|
9
|
+
from timezone_field import TimeZoneField
|
|
9
10
|
|
|
10
11
|
from nautobot.core.api.exceptions import SerializerNotFound
|
|
11
12
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
@@ -181,6 +182,7 @@ class ComputedFieldFilterSet(BaseFilterSet):
|
|
|
181
182
|
"description": "icontains",
|
|
182
183
|
"content_type__app_label": "icontains",
|
|
183
184
|
"content_type__model": "icontains",
|
|
185
|
+
"grouping": "icontains",
|
|
184
186
|
"template": "icontains",
|
|
185
187
|
"fallback_value": "icontains",
|
|
186
188
|
},
|
|
@@ -192,6 +194,7 @@ class ComputedFieldFilterSet(BaseFilterSet):
|
|
|
192
194
|
fields = (
|
|
193
195
|
"content_type",
|
|
194
196
|
"key",
|
|
197
|
+
"grouping",
|
|
195
198
|
"template",
|
|
196
199
|
"fallback_value",
|
|
197
200
|
"weight",
|
|
@@ -422,6 +425,7 @@ class CustomFieldFilterSet(BaseFilterSet):
|
|
|
422
425
|
filter_predicates={
|
|
423
426
|
"label": "icontains",
|
|
424
427
|
"description": "icontains",
|
|
428
|
+
"grouping": "icontains",
|
|
425
429
|
},
|
|
426
430
|
)
|
|
427
431
|
content_types = ContentTypeMultipleChoiceFilter(
|
|
@@ -430,7 +434,7 @@ class CustomFieldFilterSet(BaseFilterSet):
|
|
|
430
434
|
|
|
431
435
|
class Meta:
|
|
432
436
|
model = CustomField
|
|
433
|
-
fields = ["id", "content_types", "label", "required", "filter_logic", "weight"]
|
|
437
|
+
fields = ["id", "content_types", "label", "grouping", "required", "filter_logic", "weight"]
|
|
434
438
|
|
|
435
439
|
|
|
436
440
|
class CustomFieldChoiceFilterSet(BaseFilterSet):
|
|
@@ -908,6 +912,7 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
908
912
|
"job_model__name": "icontains",
|
|
909
913
|
"name": "icontains",
|
|
910
914
|
"user__username": "icontains",
|
|
915
|
+
"scheduled_job__name": "icontains",
|
|
911
916
|
},
|
|
912
917
|
)
|
|
913
918
|
job_model = NaturalKeyOrPKMultipleChoiceFilter(
|
|
@@ -919,11 +924,16 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
919
924
|
queryset=Job.objects.all(),
|
|
920
925
|
label="Job (ID) - Deprecated (use job_model filter)",
|
|
921
926
|
)
|
|
927
|
+
scheduled_job = NaturalKeyOrPKMultipleChoiceFilter(
|
|
928
|
+
to_field_name="name",
|
|
929
|
+
queryset=ScheduledJob.objects.all(),
|
|
930
|
+
label="Scheduled Job (name or ID)",
|
|
931
|
+
)
|
|
922
932
|
status = django_filters.MultipleChoiceFilter(choices=JobResultStatusChoices, null_value=None)
|
|
923
933
|
|
|
924
934
|
class Meta:
|
|
925
935
|
model = JobResult
|
|
926
|
-
fields = ["id", "date_created", "date_done", "name", "status", "user"]
|
|
936
|
+
fields = ["id", "date_created", "date_done", "name", "status", "user", "scheduled_job"]
|
|
927
937
|
|
|
928
938
|
|
|
929
939
|
class JobLogEntryFilterSet(BaseFilterSet):
|
|
@@ -957,10 +967,15 @@ class ScheduledJobFilterSet(BaseFilterSet):
|
|
|
957
967
|
queryset=Job.objects.all(),
|
|
958
968
|
label="Job (ID) - Deprecated (use job_model filter)",
|
|
959
969
|
)
|
|
970
|
+
time_zone = django_filters.MultipleChoiceFilter(
|
|
971
|
+
choices=[(str(obj), name) for obj, name in TimeZoneField().choices],
|
|
972
|
+
label="Time zone",
|
|
973
|
+
null_value="",
|
|
974
|
+
)
|
|
960
975
|
|
|
961
976
|
class Meta:
|
|
962
977
|
model = ScheduledJob
|
|
963
|
-
fields = ["id", "name", "total_run_count", "start_time", "last_run_at"]
|
|
978
|
+
fields = ["id", "name", "total_run_count", "start_time", "last_run_at", "time_zone"]
|
|
964
979
|
|
|
965
980
|
|
|
966
981
|
#
|
nautobot/extras/forms/base.py
CHANGED
|
@@ -26,11 +26,12 @@ __all__ = (
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class NautobotModelForm(
|
|
29
|
+
BootstrapMixin,
|
|
30
|
+
# The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
|
|
29
31
|
CustomFieldModelFormMixin,
|
|
30
32
|
DynamicGroupModelFormMixin,
|
|
31
33
|
NoteModelFormMixin,
|
|
32
34
|
RelationshipModelFormMixin,
|
|
33
|
-
BootstrapMixin,
|
|
34
35
|
):
|
|
35
36
|
"""
|
|
36
37
|
This class exists to combine common functionality and is used to inherit from throughout the
|
|
@@ -40,9 +41,10 @@ class NautobotModelForm(
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
class NautobotFilterForm(
|
|
43
|
-
ContactTeamModelFilterFormMixin,
|
|
44
44
|
BootstrapMixin,
|
|
45
|
-
|
|
45
|
+
# The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
|
|
46
|
+
ContactTeamModelFilterFormMixin,
|
|
47
|
+
CustomFieldModelFilterFormMixin,
|
|
46
48
|
RelationshipModelFilterFormMixin,
|
|
47
49
|
):
|
|
48
50
|
"""
|
|
@@ -53,6 +55,10 @@ class NautobotFilterForm(
|
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
class NautobotBulkEditForm(
|
|
56
|
-
BootstrapMixin,
|
|
58
|
+
BootstrapMixin,
|
|
59
|
+
# The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
|
|
60
|
+
CustomFieldModelBulkEditFormMixin,
|
|
61
|
+
NoteModelBulkEditFormMixin,
|
|
62
|
+
RelationshipModelBulkEditFormMixin,
|
|
57
63
|
):
|
|
58
64
|
"""Base class for bulk-edit forms for models that support relationships, custom fields and notes."""
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -219,6 +219,7 @@ class ComputedFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
219
219
|
fields = (
|
|
220
220
|
"content_type",
|
|
221
221
|
"label",
|
|
222
|
+
"grouping",
|
|
222
223
|
"key",
|
|
223
224
|
"description",
|
|
224
225
|
"template",
|
|
@@ -1285,6 +1286,12 @@ class JobResultFilterForm(BootstrapMixin, forms.Form):
|
|
|
1285
1286
|
required=False,
|
|
1286
1287
|
widget=StaticSelect2Multiple(),
|
|
1287
1288
|
)
|
|
1289
|
+
scheduled_job = DynamicModelMultipleChoiceField(
|
|
1290
|
+
label="Scheduled Job",
|
|
1291
|
+
queryset=ScheduledJob.objects.all(),
|
|
1292
|
+
required=False,
|
|
1293
|
+
to_field_name="name",
|
|
1294
|
+
)
|
|
1288
1295
|
|
|
1289
1296
|
|
|
1290
1297
|
class ScheduledJobFilterForm(BootstrapMixin, forms.Form):
|
nautobot/extras/forms/mixins.py
CHANGED
|
@@ -174,7 +174,7 @@ class DynamicGroupModelFormMixin(forms.ModelForm):
|
|
|
174
174
|
|
|
175
175
|
def __init__(self, *args, **kwargs):
|
|
176
176
|
super().__init__(*args, **kwargs)
|
|
177
|
-
if self._meta.model
|
|
177
|
+
if getattr(self._meta.model, "is_dynamic_group_associable_model", False):
|
|
178
178
|
self.fields["dynamic_groups"] = DynamicModelMultipleChoiceField(
|
|
179
179
|
required=False,
|
|
180
180
|
initial=self.instance.dynamic_groups if self.instance else None,
|
|
@@ -193,7 +193,7 @@ class DynamicGroupModelFormMixin(forms.ModelForm):
|
|
|
193
193
|
|
|
194
194
|
def save(self, commit=True):
|
|
195
195
|
obj = super().save(commit=commit)
|
|
196
|
-
if commit and obj
|
|
196
|
+
if commit and getattr(obj, "is_dynamic_group_associable_model", False):
|
|
197
197
|
current_groups = set(obj.dynamic_groups.filter(group_type=DynamicGroupTypeChoices.TYPE_STATIC))
|
|
198
198
|
for dynamic_group in set(self.cleaned_data.get("dynamic_groups")).difference(current_groups):
|
|
199
199
|
dynamic_group.add_members([obj])
|
nautobot/extras/homepage.py
CHANGED
|
@@ -7,14 +7,24 @@ def get_job_results(request):
|
|
|
7
7
|
"""Callback function to collect job history for panel."""
|
|
8
8
|
return (
|
|
9
9
|
JobResult.objects.filter(status__in=JobResultStatusChoices.READY_STATES)
|
|
10
|
-
.
|
|
10
|
+
.restrict(request.user, "view")
|
|
11
|
+
.only("id", "name", "status", "date_done", "user")
|
|
11
12
|
.order_by("-date_done")[:10]
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def get_changelog(request):
|
|
16
17
|
"""Callback function to collect changelog for panel."""
|
|
17
|
-
return ObjectChange.objects.restrict(request.user, "view")
|
|
18
|
+
return ObjectChange.objects.restrict(request.user, "view").only(
|
|
19
|
+
"id",
|
|
20
|
+
"action",
|
|
21
|
+
"changed_object",
|
|
22
|
+
"changed_object_id",
|
|
23
|
+
"changed_object_type",
|
|
24
|
+
"object_repr",
|
|
25
|
+
"user_name",
|
|
26
|
+
"time",
|
|
27
|
+
)[:15]
|
|
18
28
|
|
|
19
29
|
|
|
20
30
|
layout = (
|
nautobot/extras/jobs.py
CHANGED
|
@@ -20,7 +20,7 @@ from django.conf import settings
|
|
|
20
20
|
from django.contrib.auth import get_user_model
|
|
21
21
|
from django.core.exceptions import ObjectDoesNotExist
|
|
22
22
|
from django.core.files.base import ContentFile
|
|
23
|
-
from django.core.files.uploadedfile import
|
|
23
|
+
from django.core.files.uploadedfile import UploadedFile
|
|
24
24
|
from django.core.validators import RegexValidator
|
|
25
25
|
from django.db.models import Model
|
|
26
26
|
from django.db.models.query import QuerySet
|
|
@@ -539,7 +539,7 @@ class BaseJob:
|
|
|
539
539
|
elif isinstance(value, Model):
|
|
540
540
|
return_data[field_name] = value.pk
|
|
541
541
|
# FileVar (Save each FileVar as a FileProxy)
|
|
542
|
-
elif isinstance(value,
|
|
542
|
+
elif isinstance(value, UploadedFile):
|
|
543
543
|
return_data[field_name] = BaseJob._save_file_to_proxy(value)
|
|
544
544
|
# IPAddressVar, IPAddressWithMaskVar, IPNetworkVar
|
|
545
545
|
elif isinstance(value, netaddr.ip.BaseIP):
|
|
@@ -32,6 +32,7 @@ STATUS_CHOICESET_MAP = {
|
|
|
32
32
|
"ipam.IPAddress": ipam_choices.IPAddressStatusChoices,
|
|
33
33
|
"ipam.Prefix": ipam_choices.PrefixStatusChoices,
|
|
34
34
|
"ipam.VLAN": ipam_choices.VLANStatusChoices,
|
|
35
|
+
"ipam.VRF": ipam_choices.VRFStatusChoices,
|
|
35
36
|
"virtualization.VirtualMachine": vm_choices.VirtualMachineStatusChoices,
|
|
36
37
|
"virtualization.VMInterface": vm_choices.VMInterfaceStatusChoices,
|
|
37
38
|
}
|
|
@@ -48,6 +49,7 @@ STATUS_COLOR_MAP = {
|
|
|
48
49
|
"Decommissioning": ColorChoices.COLOR_AMBER,
|
|
49
50
|
"Deprecated": ColorChoices.COLOR_RED,
|
|
50
51
|
"Deprovisioning": ColorChoices.COLOR_AMBER,
|
|
52
|
+
"Down": ColorChoices.COLOR_AMBER,
|
|
51
53
|
"End-of-Life": ColorChoices.COLOR_RED,
|
|
52
54
|
"Extended Support": ColorChoices.COLOR_CYAN,
|
|
53
55
|
"Failed": ColorChoices.COLOR_RED,
|
|
@@ -76,6 +78,7 @@ STATUS_DESCRIPTION_MAP = {
|
|
|
76
78
|
"Decommissioning": "Unit is being decommissioned",
|
|
77
79
|
"Deprecated": "Unit has been deprecated",
|
|
78
80
|
"Deprovisioning": "Circuit is being deprovisioned",
|
|
81
|
+
"Down": "VRF is down",
|
|
79
82
|
"End-of-Life": "Unit has reached end-of-life",
|
|
80
83
|
"Extended Support": "Software is in extended support",
|
|
81
84
|
"Failed": "Unit has failed",
|
|
@@ -72,7 +72,7 @@ class Migration(migrations.Migration):
|
|
|
72
72
|
("last_updated", models.DateTimeField(auto_now=True, null=True)),
|
|
73
73
|
(
|
|
74
74
|
"scoped_fields",
|
|
75
|
-
nautobot.core.models.fields.JSONArrayField(base_field=models.CharField(max_length=255)),
|
|
75
|
+
nautobot.core.models.fields.JSONArrayField(base_field=models.CharField(max_length=255), blank=True),
|
|
76
76
|
),
|
|
77
77
|
("_value", models.JSONField(blank=True, null=True)),
|
|
78
78
|
("assigned_object_id", models.UUIDField(db_index=True)),
|
|
@@ -91,7 +91,7 @@ class Migration(migrations.Migration):
|
|
|
91
91
|
blank=True,
|
|
92
92
|
null=True,
|
|
93
93
|
on_delete=django.db.models.deletion.PROTECT,
|
|
94
|
-
related_name="
|
|
94
|
+
related_name="object_metadata",
|
|
95
95
|
to="extras.contact",
|
|
96
96
|
),
|
|
97
97
|
),
|
|
@@ -99,7 +99,7 @@ class Migration(migrations.Migration):
|
|
|
99
99
|
"metadata_type",
|
|
100
100
|
models.ForeignKey(
|
|
101
101
|
on_delete=django.db.models.deletion.PROTECT,
|
|
102
|
-
related_name="
|
|
102
|
+
related_name="object_metadata",
|
|
103
103
|
to="extras.metadatatype",
|
|
104
104
|
),
|
|
105
105
|
),
|
|
@@ -109,7 +109,7 @@ class Migration(migrations.Migration):
|
|
|
109
109
|
blank=True,
|
|
110
110
|
null=True,
|
|
111
111
|
on_delete=django.db.models.deletion.PROTECT,
|
|
112
|
-
related_name="
|
|
112
|
+
related_name="object_metadata",
|
|
113
113
|
to="extras.team",
|
|
114
114
|
),
|
|
115
115
|
),
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 4.2.15 on 2024-08-09 14:28
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("extras", "0113_saved_views"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AddField(
|
|
13
|
+
model_name="computedfield",
|
|
14
|
+
name="grouping",
|
|
15
|
+
field=models.CharField(blank=True, max_length=255),
|
|
16
|
+
),
|
|
17
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 4.2.15 on 2024-08-19 13:44
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
from django.utils import timezone
|
|
5
|
+
import timezone_field.fields
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
dependencies = [
|
|
10
|
+
("extras", "0114_computedfield_grouping"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="scheduledjob",
|
|
16
|
+
name="time_zone",
|
|
17
|
+
field=timezone_field.fields.TimeZoneField(default=timezone.get_default_timezone_name),
|
|
18
|
+
),
|
|
19
|
+
migrations.AlterModelOptions(
|
|
20
|
+
name="scheduledjob",
|
|
21
|
+
options={"ordering": ["name"]},
|
|
22
|
+
),
|
|
23
|
+
]
|
|
@@ -88,6 +88,11 @@ class ComputedField(
|
|
|
88
88
|
help_text="Internal field name. Please use underscores rather than dashes in this key.",
|
|
89
89
|
slugify_function=slugify_dashes_to_underscores,
|
|
90
90
|
)
|
|
91
|
+
grouping = models.CharField(
|
|
92
|
+
max_length=CHARFIELD_MAX_LENGTH,
|
|
93
|
+
blank=True,
|
|
94
|
+
help_text="Human-readable grouping that this computed field belongs to.",
|
|
95
|
+
)
|
|
91
96
|
label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, help_text="Name of the field as displayed to users")
|
|
92
97
|
description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
|
|
93
98
|
template = models.TextField(max_length=500, help_text="Jinja2 template code for field value")
|
|
@@ -295,6 +300,55 @@ class CustomFieldModel(models.Model):
|
|
|
295
300
|
return computed_field.render(context={"obj": self})
|
|
296
301
|
return computed_field.template
|
|
297
302
|
|
|
303
|
+
def get_computed_fields_grouping_basic(self):
|
|
304
|
+
"""
|
|
305
|
+
This method exists to help call get_computed_field_groupings() in templates where a function argument (advanced_ui) cannot be specified.
|
|
306
|
+
Return a dictonary of computed fields grouped by the same grouping in the form
|
|
307
|
+
{
|
|
308
|
+
<grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
|
|
309
|
+
...
|
|
310
|
+
<grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
|
|
311
|
+
...
|
|
312
|
+
}
|
|
313
|
+
which have advanced_ui set to False
|
|
314
|
+
"""
|
|
315
|
+
return self.get_computed_fields_grouping(advanced_ui=False)
|
|
316
|
+
|
|
317
|
+
def get_computed_fields_grouping_advanced(self):
|
|
318
|
+
"""
|
|
319
|
+
This method exists to help call get_computed_field_groupings() in templates where a function argument (advanced_ui) cannot be specified.
|
|
320
|
+
Return a dictonary of computed fields grouped by the same grouping in the form
|
|
321
|
+
{
|
|
322
|
+
<grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
|
|
323
|
+
...
|
|
324
|
+
<grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
|
|
325
|
+
...
|
|
326
|
+
}
|
|
327
|
+
which have advanced_ui set to True
|
|
328
|
+
"""
|
|
329
|
+
return self.get_computed_fields_grouping(advanced_ui=True)
|
|
330
|
+
|
|
331
|
+
def get_computed_fields_grouping(self, advanced_ui=None):
|
|
332
|
+
"""
|
|
333
|
+
Return a dictonary of computed fields grouped by the same grouping in the form
|
|
334
|
+
{
|
|
335
|
+
<grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
|
|
336
|
+
...
|
|
337
|
+
<grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
|
|
338
|
+
...
|
|
339
|
+
}
|
|
340
|
+
"""
|
|
341
|
+
record = {}
|
|
342
|
+
computed_fields = ComputedField.objects.get_for_model(self)
|
|
343
|
+
if advanced_ui is not None:
|
|
344
|
+
computed_fields = computed_fields.filter(advanced_ui=advanced_ui)
|
|
345
|
+
|
|
346
|
+
for field in computed_fields:
|
|
347
|
+
data = (field, field.render(context={"obj": self}))
|
|
348
|
+
record.setdefault(field.grouping, []).append(data)
|
|
349
|
+
record = dict(sorted(record.items()))
|
|
350
|
+
return record
|
|
351
|
+
|
|
298
352
|
def get_computed_fields(self, label_as_key=False, advanced_ui=None):
|
|
299
353
|
"""
|
|
300
354
|
Return a dictionary of all computed fields and their rendered values for this model.
|