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/cloud/factory.py
CHANGED
|
@@ -24,6 +24,7 @@ class CloudAccountFactory(PrimaryModelFactory):
|
|
|
24
24
|
has_description = NautobotBoolIterator()
|
|
25
25
|
description = factory.Maybe("has_description", factory.Faker("sentence"), "")
|
|
26
26
|
provider = random_instance(Manufacturer, allow_null=False)
|
|
27
|
+
# TODO: once SecretsGroupFactory is implemented:
|
|
27
28
|
# has_secrets_group = NautobotBoolIterator()
|
|
28
29
|
# secrets_group = factory.Maybe(
|
|
29
30
|
# "has_secrets_group",
|
|
@@ -40,6 +41,7 @@ class CloudResourceTypeFactory(PrimaryModelFactory):
|
|
|
40
41
|
name = factory.LazyAttributeSequence(lambda o, n: f"{o.provider.name} CloudResourceType {n + 1}")
|
|
41
42
|
has_description = NautobotBoolIterator()
|
|
42
43
|
description = factory.Maybe("has_description", factory.Faker("sentence"), "")
|
|
44
|
+
# TODO: if we add a `config_schema` here, then CloudNetworkFactory and CloudServiceFactory have to follow it...
|
|
43
45
|
|
|
44
46
|
@factory.post_generation
|
|
45
47
|
def content_types(self, create, extracted, **kwargs):
|
nautobot/cloud/filters.py
CHANGED
|
@@ -74,6 +74,8 @@ class CloudNetworkFilterSet(NautobotFilterSet):
|
|
|
74
74
|
filter_predicates={
|
|
75
75
|
"name": "icontains",
|
|
76
76
|
"description": "icontains",
|
|
77
|
+
"parent__name": "icontains",
|
|
78
|
+
"parent__description": "icontains",
|
|
77
79
|
"cloud_account__name": "icontains",
|
|
78
80
|
"cloud_account__description": "icontains",
|
|
79
81
|
"cloud_resource_type__name": "icontains",
|
|
@@ -96,6 +98,7 @@ class CloudNetworkFilterSet(NautobotFilterSet):
|
|
|
96
98
|
queryset=models.CloudNetwork.objects.all(),
|
|
97
99
|
label="Parent cloud network (name or ID)",
|
|
98
100
|
)
|
|
101
|
+
prefixes = django_filters.ModelMultipleChoiceFilter(queryset=Prefix.objects.all())
|
|
99
102
|
|
|
100
103
|
class Meta:
|
|
101
104
|
model = models.CloudNetwork
|
nautobot/cloud/forms.py
CHANGED
|
@@ -26,7 +26,7 @@ class CloudAccountForm(NautobotModelForm):
|
|
|
26
26
|
queryset=Manufacturer.objects.all(),
|
|
27
27
|
help_text="The Manufacturer instance which represents the Cloud Provider",
|
|
28
28
|
)
|
|
29
|
-
secrets_group = DynamicModelChoiceField(queryset=SecretsGroup.objects.all())
|
|
29
|
+
secrets_group = DynamicModelChoiceField(queryset=SecretsGroup.objects.all(), required=False)
|
|
30
30
|
|
|
31
31
|
class Meta:
|
|
32
32
|
model = CloudAccount
|
|
@@ -94,6 +94,7 @@ class CloudNetworkForm(NautobotModelForm):
|
|
|
94
94
|
)
|
|
95
95
|
parent = DynamicModelChoiceField(
|
|
96
96
|
queryset=CloudNetwork.objects.all(),
|
|
97
|
+
query_params={"parent__isnull": True},
|
|
97
98
|
required=False,
|
|
98
99
|
)
|
|
99
100
|
namespace = DynamicModelChoiceField(queryset=Namespace.objects.all(), required=False)
|
|
@@ -187,7 +188,12 @@ class CloudNetworkFilterForm(NautobotFilterForm):
|
|
|
187
188
|
cloud_account = DynamicModelMultipleChoiceField(
|
|
188
189
|
queryset=CloudAccount.objects.all(), to_field_name="name", required=False
|
|
189
190
|
)
|
|
190
|
-
parent = DynamicModelMultipleChoiceField(
|
|
191
|
+
parent = DynamicModelMultipleChoiceField(
|
|
192
|
+
queryset=CloudNetwork.objects.all(),
|
|
193
|
+
query_params={"parent__isnull": True},
|
|
194
|
+
to_field_name="name",
|
|
195
|
+
required=False,
|
|
196
|
+
)
|
|
191
197
|
tags = TagFilterField(model)
|
|
192
198
|
|
|
193
199
|
|
|
@@ -271,7 +271,7 @@ class Migration(migrations.Migration):
|
|
|
271
271
|
field=models.ForeignKey(
|
|
272
272
|
blank=True,
|
|
273
273
|
null=True,
|
|
274
|
-
on_delete=django.db.models.deletion.
|
|
274
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
275
275
|
related_name="children",
|
|
276
276
|
to="cloud.cloudnetwork",
|
|
277
277
|
),
|
nautobot/cloud/models.py
CHANGED
|
@@ -14,7 +14,6 @@ from nautobot.extras.utils import FeatureQuery, extras_features
|
|
|
14
14
|
@extras_features(
|
|
15
15
|
"custom_links",
|
|
16
16
|
"custom_validators",
|
|
17
|
-
"dynamic_groups",
|
|
18
17
|
"export_templates",
|
|
19
18
|
"graphql",
|
|
20
19
|
"webhooks",
|
|
@@ -137,7 +136,7 @@ class CloudNetwork(CloudResourceTypeMixin, PrimaryModel):
|
|
|
137
136
|
description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
|
|
138
137
|
cloud_account = models.ForeignKey(to=CloudAccount, on_delete=models.PROTECT, related_name="cloud_networks")
|
|
139
138
|
parent = models.ForeignKey(
|
|
140
|
-
to="cloud.CloudNetwork", on_delete=models.
|
|
139
|
+
to="cloud.CloudNetwork", on_delete=models.PROTECT, blank=True, null=True, related_name="children"
|
|
141
140
|
)
|
|
142
141
|
prefixes = models.ManyToManyField(
|
|
143
142
|
blank=True,
|
nautobot/cloud/tables.py
CHANGED
|
@@ -7,9 +7,8 @@ from nautobot.core.tables import (
|
|
|
7
7
|
TagColumn,
|
|
8
8
|
ToggleColumn,
|
|
9
9
|
)
|
|
10
|
-
from nautobot.tenancy.tables import TenantColumn
|
|
11
10
|
|
|
12
|
-
from .models import CloudAccount, CloudNetwork,
|
|
11
|
+
from .models import CloudAccount, CloudNetwork, CloudResourceType, CloudService
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class CloudAccountTable(BaseTable):
|
|
@@ -95,21 +94,6 @@ class CloudNetworkTable(BaseTable):
|
|
|
95
94
|
)
|
|
96
95
|
|
|
97
96
|
|
|
98
|
-
class CloudNetworkPrefixAssignmentTable(BaseTable):
|
|
99
|
-
cloud_network = tables.Column(
|
|
100
|
-
verbose_name="Cloud Network",
|
|
101
|
-
linkify=lambda record: record.cloud_network.get_absolute_url(),
|
|
102
|
-
accessor="cloud_network.name",
|
|
103
|
-
)
|
|
104
|
-
prefix = tables.Column(linkify=True)
|
|
105
|
-
rd = tables.Column(accessor="vrf.rd", verbose_name="RD")
|
|
106
|
-
tenant = TenantColumn(accessor="vrf.tenant")
|
|
107
|
-
|
|
108
|
-
class Meta(BaseTable.Meta):
|
|
109
|
-
model = CloudNetworkPrefixAssignment
|
|
110
|
-
fields = ("cloud_network", "prefix", "rd", "tenant")
|
|
111
|
-
|
|
112
|
-
|
|
113
97
|
class CloudResourceTypeTable(BaseTable):
|
|
114
98
|
pk = ToggleColumn()
|
|
115
99
|
name = tables.Column(linkify=True)
|
|
@@ -100,13 +100,7 @@
|
|
|
100
100
|
</tr>
|
|
101
101
|
<tr>
|
|
102
102
|
<td>Parent</td>
|
|
103
|
-
<td>
|
|
104
|
-
{% if object.parent %}
|
|
105
|
-
{{ object.parent|hyperlinked_object }}
|
|
106
|
-
{% else %}
|
|
107
|
-
<span class="text-muted">—</span>
|
|
108
|
-
{% endif %}
|
|
109
|
-
</td>
|
|
103
|
+
<td>{{ object.parent|hyperlinked_object }}</td>
|
|
110
104
|
</tr>
|
|
111
105
|
<tr>
|
|
112
106
|
<td>Description</td>
|
|
@@ -60,6 +60,17 @@
|
|
|
60
60
|
</div>
|
|
61
61
|
{% endblock content_left_page %}
|
|
62
62
|
|
|
63
|
+
{% block content_right_page %}
|
|
64
|
+
<div class="panel panel-default">
|
|
65
|
+
<div class="panel-heading">
|
|
66
|
+
<strong>Config Schema</strong>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="panel-body">
|
|
69
|
+
<pre>{{ object.config_schema|render_json }}</pre>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{% endblock content_right_page %}
|
|
73
|
+
|
|
63
74
|
{% block extra_tab_content %}
|
|
64
75
|
{% if networks_count %}
|
|
65
76
|
<div id="networks" role="tabpanel" class="tab-pane {% if not active_tab and not request.GET.tab or request.GET.tab == "networks" %}active{% else %}fade{% endif %}">
|
|
@@ -65,6 +65,18 @@ class CloudNetworkTestCase(FilterTestCases.NameOnlyFilterTestCase):
|
|
|
65
65
|
("parent", "parent__id"),
|
|
66
66
|
("parent", "parent__name"),
|
|
67
67
|
]
|
|
68
|
+
exclude_q_filter_predicates = [
|
|
69
|
+
"parent__name",
|
|
70
|
+
"parent__description",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
def _get_relevant_filterset_queryset(self, queryset, *filter_params):
|
|
74
|
+
queryset = super()._get_relevant_filterset_queryset(queryset, *filter_params)
|
|
75
|
+
if "name" in filter_params or "description" in filter_params:
|
|
76
|
+
# Only select an instance with no children as otherwise the search will also match the children,
|
|
77
|
+
# due to `parent__name` and `parent__description` also being search parameters
|
|
78
|
+
queryset = queryset.filter(children__isnull=True)
|
|
79
|
+
return queryset
|
|
68
80
|
|
|
69
81
|
|
|
70
82
|
class CloudNetworkPrefixAssignmentTestCase(FilterTestCases.FilterTestCase):
|
|
@@ -2,6 +2,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
2
2
|
|
|
3
3
|
from nautobot.cloud.models import CloudAccount, CloudNetwork, CloudResourceType, CloudService
|
|
4
4
|
from nautobot.core.testing import ViewTestCases
|
|
5
|
+
from nautobot.core.testing.utils import post_data
|
|
5
6
|
from nautobot.dcim.models import Manufacturer
|
|
6
7
|
from nautobot.extras.models import SecretsGroup, Tag
|
|
7
8
|
|
|
@@ -36,6 +37,22 @@ class CloudAccountTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
36
37
|
"comments": "New comments",
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
def test_post_without_secrets_group(self):
|
|
41
|
+
"""Assert Secrets Group form field is not required: Fix for https://github.com/nautobot/nautobot/issues/6096"""
|
|
42
|
+
self.add_permissions("cloud.add_cloudaccount", "dcim.view_manufacturer")
|
|
43
|
+
form_data = {
|
|
44
|
+
"name": "New Cloud Account 2",
|
|
45
|
+
"account_number": "8928371982311",
|
|
46
|
+
"provider": Manufacturer.objects.first().pk,
|
|
47
|
+
"description": "A new cloud account",
|
|
48
|
+
}
|
|
49
|
+
request = {
|
|
50
|
+
"path": self._get_url("add"),
|
|
51
|
+
"data": post_data(form_data),
|
|
52
|
+
}
|
|
53
|
+
self.assertHttpStatus(self.client.post(**request), 302)
|
|
54
|
+
self.assertTrue(CloudAccount.objects.filter(name="New Cloud Account 2").exists())
|
|
55
|
+
|
|
39
56
|
|
|
40
57
|
class CloudNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
41
58
|
model = CloudNetwork
|
nautobot/cloud/views.py
CHANGED
|
@@ -59,7 +59,7 @@ class CloudNetworkUIViewSet(NautobotUIViewSet):
|
|
|
59
59
|
context = super().get_extra_context(request, instance)
|
|
60
60
|
if self.action == "retrieve":
|
|
61
61
|
prefixes = instance.prefixes.restrict(request.user, "view")
|
|
62
|
-
prefixes_table = PrefixTable(prefixes.select_related("namespace"))
|
|
62
|
+
prefixes_table = PrefixTable(prefixes.select_related("namespace"), hide_hierarchy_ui=True)
|
|
63
63
|
prefixes_table.columns.hide("location_count")
|
|
64
64
|
prefixes_table.columns.hide("vlan")
|
|
65
65
|
|
nautobot/core/celery/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ from celery import Celery, shared_task, signals
|
|
|
8
8
|
from celery.app.log import TaskFormatter
|
|
9
9
|
from celery.utils.log import get_logger
|
|
10
10
|
from django.conf import settings
|
|
11
|
-
from django.db.utils import ProgrammingError
|
|
11
|
+
from django.db.utils import OperationalError, ProgrammingError
|
|
12
12
|
from django.utils.functional import SimpleLazyObject
|
|
13
13
|
from django.utils.module_loading import import_string
|
|
14
14
|
from kombu.serialization import register
|
|
@@ -55,7 +55,10 @@ def import_jobs(sender=None, **kwargs):
|
|
|
55
55
|
|
|
56
56
|
try:
|
|
57
57
|
_import_jobs_from_git_repositories()
|
|
58
|
-
except
|
|
58
|
+
except (
|
|
59
|
+
OperationalError, # Database not present, as may be the case when running pylint-nautobot
|
|
60
|
+
ProgrammingError, # Database not ready yet, as may be the case on initial startup and migration
|
|
61
|
+
):
|
|
59
62
|
pass
|
|
60
63
|
|
|
61
64
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from collections.abc import Mapping
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
import logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
@@ -20,7 +21,10 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
20
21
|
|
|
21
22
|
def __init__(self, model, app=None):
|
|
22
23
|
"""Initialize the model entry."""
|
|
24
|
+
# copy-paste from django_celery_beat.schedulers
|
|
23
25
|
self.app = app or current_app._get_current_object()
|
|
26
|
+
|
|
27
|
+
# Nautobot-specific logic
|
|
24
28
|
self.name = f"{model.name}_{model.pk}"
|
|
25
29
|
self.task = "nautobot.extras.jobs.run_job"
|
|
26
30
|
try:
|
|
@@ -33,6 +37,8 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
33
37
|
except (TypeError, ValueError) as exc:
|
|
34
38
|
logger.exception("Removing schedule %s for argument deserialization error: %s", self.name, exc)
|
|
35
39
|
self._disable(model)
|
|
40
|
+
|
|
41
|
+
# copy-paste from django_celery_beat.schedulers
|
|
36
42
|
try:
|
|
37
43
|
self.schedule = model.schedule
|
|
38
44
|
except model.DoesNotExist:
|
|
@@ -42,6 +48,7 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
42
48
|
)
|
|
43
49
|
self._disable(model)
|
|
44
50
|
|
|
51
|
+
# Nautobot-specific logic
|
|
45
52
|
self.options = {"nautobot_job_scheduled_job_id": model.id, "headers": {}}
|
|
46
53
|
|
|
47
54
|
if model.user:
|
|
@@ -65,14 +72,25 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
65
72
|
if isinstance(model.celery_kwargs, Mapping):
|
|
66
73
|
self.options.update(model.celery_kwargs)
|
|
67
74
|
|
|
75
|
+
# copy-paste from django_celery_beat.schedulers
|
|
68
76
|
self.total_run_count = model.total_run_count
|
|
69
77
|
self.model = model
|
|
70
78
|
|
|
71
79
|
if not model.last_run_at:
|
|
72
80
|
model.last_run_at = self._default_now()
|
|
81
|
+
# if last_run_at is not set and
|
|
82
|
+
# model.start_time last_run_at should be in way past.
|
|
83
|
+
# This will trigger the job to run at start_time
|
|
84
|
+
# and avoid the heap block.
|
|
85
|
+
if model.start_time:
|
|
86
|
+
model.last_run_at = model.last_run_at - datetime.timedelta(days=365 * 30)
|
|
73
87
|
|
|
74
88
|
self.last_run_at = model.last_run_at
|
|
75
89
|
|
|
90
|
+
def _default_now(self):
|
|
91
|
+
"""Instead of using self.app.timezone, use the timezone specific to this schedule entry."""
|
|
92
|
+
return datetime.now(self.model.time_zone)
|
|
93
|
+
|
|
76
94
|
|
|
77
95
|
class NautobotDatabaseScheduler(DatabaseScheduler):
|
|
78
96
|
"""
|
nautobot/core/filters.py
CHANGED
|
@@ -6,9 +6,11 @@ from django import forms as django_forms
|
|
|
6
6
|
from django.conf import settings
|
|
7
7
|
from django.db import models
|
|
8
8
|
from django.forms.utils import ErrorDict, ErrorList
|
|
9
|
+
from django.utils.encoding import force_str
|
|
10
|
+
from django.utils.text import capfirst
|
|
9
11
|
import django_filters
|
|
10
12
|
from django_filters.constants import EMPTY_VALUES
|
|
11
|
-
from django_filters.utils import get_model_field, resolve_field
|
|
13
|
+
from django_filters.utils import get_model_field, label_for_filter, resolve_field, verbose_lookup_expr
|
|
12
14
|
from drf_spectacular.types import OpenApiTypes
|
|
13
15
|
from drf_spectacular.utils import extend_schema_field
|
|
14
16
|
|
|
@@ -720,6 +722,18 @@ class BaseFilterSet(django_filters.FilterSet):
|
|
|
720
722
|
# Of course setting the negation of the existing filter's exclude attribute handles both cases
|
|
721
723
|
new_filter.exclude = not filter_field.exclude
|
|
722
724
|
|
|
725
|
+
# If the base filter_field has a custom label, django_filters won't adjust it for the new_filter lookup,
|
|
726
|
+
# so we have to do it.
|
|
727
|
+
if filter_field.label and filter_field.label != label_for_filter(
|
|
728
|
+
cls.Meta.model, filter_field.field_name, filter_field.lookup_expr, filter_field.exclude
|
|
729
|
+
):
|
|
730
|
+
# Lightly adjusted from label_for_filter() implementation:
|
|
731
|
+
verbose_expression = ["exclude", filter_field.label] if new_filter.exclude else [filter_field.label]
|
|
732
|
+
if isinstance(lookup_expr, str):
|
|
733
|
+
verbose_expression.append(verbose_lookup_expr(lookup_expr))
|
|
734
|
+
verbose_expression = [force_str(part) for part in verbose_expression if part]
|
|
735
|
+
new_filter.label = capfirst(" ".join(verbose_expression))
|
|
736
|
+
|
|
723
737
|
magic_filters[new_filter_name] = new_filter
|
|
724
738
|
|
|
725
739
|
return magic_filters
|
nautobot/core/forms/forms.py
CHANGED
|
@@ -59,6 +59,13 @@ class AddressFieldMixin(forms.ModelForm):
|
|
|
59
59
|
class BootstrapMixin(forms.BaseForm):
|
|
60
60
|
"""
|
|
61
61
|
Add the base Bootstrap CSS classes to form elements.
|
|
62
|
+
|
|
63
|
+
Note that this only applies to form fields that are:
|
|
64
|
+
|
|
65
|
+
1. statically defined on the form class at declaration time, or
|
|
66
|
+
2. dynamically added to the form at init time by a class **later in the MRO than this mixin**.
|
|
67
|
+
|
|
68
|
+
If a class earlier in the MRO adds its own fields, it will have to ensure that the widgets are correctly configured.
|
|
62
69
|
"""
|
|
63
70
|
|
|
64
71
|
def __init__(self, *args, **kwargs):
|
|
@@ -73,8 +80,9 @@ class BootstrapMixin(forms.BaseForm):
|
|
|
73
80
|
|
|
74
81
|
for field in self.fields.values():
|
|
75
82
|
if field.widget.__class__ not in exempt_widgets:
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
css_classes = field.widget.attrs.get("class", "")
|
|
84
|
+
if "form-control" not in css_classes:
|
|
85
|
+
field.widget.attrs["class"] = " ".join([css_classes, "form-control"]).strip()
|
|
78
86
|
if field.required and not isinstance(field.widget, forms.FileInput):
|
|
79
87
|
field.widget.attrs["required"] = "required"
|
|
80
88
|
if "placeholder" not in field.widget.attrs:
|
|
@@ -64,8 +64,8 @@ def generate_filter_resolver(schema_type, resolver_name, field_name):
|
|
|
64
64
|
"""
|
|
65
65
|
filterset_class = schema_type._meta.filterset_class
|
|
66
66
|
|
|
67
|
-
def resolve_filter(self,
|
|
68
|
-
if not filterset_class:
|
|
67
|
+
def resolve_filter(self, info, **kwargs):
|
|
68
|
+
if not filterset_class or not kwargs:
|
|
69
69
|
return getattr(self, field_name).all()
|
|
70
70
|
|
|
71
71
|
# Inverse of substitution logic from get_filtering_args_from_filterset() - transform "_type" back to "type"
|
nautobot/core/graphql/schema.py
CHANGED
|
@@ -4,6 +4,7 @@ from collections import OrderedDict
|
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
6
|
from django.conf import settings
|
|
7
|
+
from django.contrib.contenttypes.fields import GenericRelation
|
|
7
8
|
from django.contrib.contenttypes.models import ContentType
|
|
8
9
|
from django.core.validators import ValidationError
|
|
9
10
|
from django.db.models import ManyToManyField
|
|
@@ -205,8 +206,7 @@ def extend_schema_type_filter(schema_type, model):
|
|
|
205
206
|
(DjangoObjectType): The extended schema_type object
|
|
206
207
|
"""
|
|
207
208
|
for field in model._meta.get_fields():
|
|
208
|
-
|
|
209
|
-
if not isinstance(field, (ManyToManyField, ManyToManyRel, ManyToOneRel)):
|
|
209
|
+
if not isinstance(field, (ManyToManyField, ManyToManyRel, ManyToOneRel, GenericRelation)):
|
|
210
210
|
continue
|
|
211
211
|
# OneToOneRel is a subclass of ManyToOneRel, but we don't want to treat it as a list
|
|
212
212
|
if isinstance(field, OneToOneRel):
|
|
@@ -374,16 +374,9 @@ def extend_schema_type_config_context(schema_type, model):
|
|
|
374
374
|
|
|
375
375
|
def extend_schema_type_global_features(schema_type, model):
|
|
376
376
|
"""
|
|
377
|
-
Extend schema_type object to have attributes and resolvers for global features (
|
|
377
|
+
Extend schema_type object to have attributes and resolvers for global features (dynamic groups, etc.).
|
|
378
378
|
"""
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
def resolve_associated_contacts(self, args):
|
|
382
|
-
return self.associated_contacts.all()
|
|
383
|
-
|
|
384
|
-
setattr(schema_type, "resolve_associated_contacts", resolve_associated_contacts)
|
|
385
|
-
schema_type._meta.fields["associated_contacts"] = graphene.Field.mounted(graphene.List(ContactAssociationType))
|
|
386
|
-
|
|
379
|
+
# associated_contacts and associated_object_metadata are handled elsewhere by extend_schema_type_filter()
|
|
387
380
|
if getattr(model, "is_dynamic_group_associable_model", False):
|
|
388
381
|
|
|
389
382
|
def resolve_dynamic_groups(self, args):
|
|
@@ -399,10 +392,9 @@ def extend_schema_type_relationships(schema_type, model):
|
|
|
399
392
|
"""Extend the schema type with attributes and resolvers corresponding
|
|
400
393
|
to the relationships associated with this model."""
|
|
401
394
|
|
|
402
|
-
ct = ContentType.objects.get_for_model(model)
|
|
403
395
|
relationships_by_side = {
|
|
404
|
-
"source": Relationship.objects.
|
|
405
|
-
"destination": Relationship.objects.
|
|
396
|
+
"source": Relationship.objects.get_for_model_source(model),
|
|
397
|
+
"destination": Relationship.objects.get_for_model_destination(model),
|
|
406
398
|
}
|
|
407
399
|
|
|
408
400
|
prefix = ""
|
nautobot/core/jobs/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import codecs
|
|
1
2
|
import contextlib
|
|
2
3
|
from io import BytesIO
|
|
3
4
|
|
|
@@ -296,7 +297,9 @@ class ImportObjects(Job):
|
|
|
296
297
|
if not csv_data and not csv_file:
|
|
297
298
|
raise RunJobTaskFailed("Either csv_data or csv_file must be provided")
|
|
298
299
|
if csv_file:
|
|
299
|
-
|
|
300
|
+
# data_encoding is utf-8 and file_encoding is utf-8-sig
|
|
301
|
+
# Bytes read from the original file are decoded according to file_encoding, and the result is encoded using data_encoding.
|
|
302
|
+
csv_bytes = codecs.EncodedFile(csv_file, "utf-8", "utf-8-sig")
|
|
300
303
|
else:
|
|
301
304
|
csv_bytes = BytesIO(csv_data.encode("utf-8"))
|
|
302
305
|
|
|
@@ -329,6 +329,8 @@ class Command(BaseCommand):
|
|
|
329
329
|
)
|
|
330
330
|
_create_batch(MetadataChoiceFactory, 100)
|
|
331
331
|
_create_batch(ObjectChangeFactory, 100)
|
|
332
|
+
_create_batch(JobResultFactory, 20)
|
|
333
|
+
_create_batch(JobLogEntryFactory, 100)
|
|
332
334
|
_create_batch(ObjectMetadataFactory, 100)
|
|
333
335
|
_create_batch(
|
|
334
336
|
ObjectMetadataFactory,
|
|
@@ -344,8 +346,6 @@ class Command(BaseCommand):
|
|
|
344
346
|
has_contact=False,
|
|
345
347
|
description="with teams",
|
|
346
348
|
)
|
|
347
|
-
_create_batch(JobResultFactory, 20)
|
|
348
|
-
_create_batch(JobLogEntryFactory, 100)
|
|
349
349
|
|
|
350
350
|
def handle(self, *args, **options):
|
|
351
351
|
if options["flush"]:
|
nautobot/core/models/__init__.py
CHANGED
|
@@ -54,11 +54,11 @@ class BaseModel(models.Model):
|
|
|
54
54
|
is_saved_view_model = False # SavedViewMixin overrides this to default True
|
|
55
55
|
is_cloud_resource_type_model = False # CloudResourceTypeMixin overrides this to default True
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
associated_object_metadata = GenericRelation(
|
|
58
58
|
"extras.ObjectMetadata",
|
|
59
59
|
content_type_field="assigned_object_type",
|
|
60
60
|
object_id_field="assigned_object_id",
|
|
61
|
-
related_query_name="
|
|
61
|
+
related_query_name="associated_object_metadata_%(app_label)s_%(class)s", # e.g. 'associated_object_metadata_dcim_device'
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
class Meta:
|
nautobot/core/settings.py
CHANGED
|
@@ -892,13 +892,24 @@ CELERY_TASK_TRACK_STARTED = True
|
|
|
892
892
|
# If enabled, a `task-sent` event will be sent for every task so tasks can be tracked before they're consumed by a worker.
|
|
893
893
|
CELERY_TASK_SEND_SENT_EVENT = True
|
|
894
894
|
|
|
895
|
+
# How many tasks a worker is allowed to reserve for its own consumption and execution.
|
|
896
|
+
# If set to zero (not recommended) a single worker can reserve all tasks even if other workers are free.
|
|
897
|
+
# For short running tasks (such as webhooks) you may want to set this to a larger number to increase throughput.
|
|
898
|
+
# Conversely, for long running tasks (such as SSoT or Golden-Config Jobs at scale) you may want to set this to 1
|
|
899
|
+
# so that a worker executing a long-running task will not prefetch other tasks, which would block their execution
|
|
900
|
+
# until the long-running task completes.
|
|
901
|
+
# https://docs.celeryq.dev/en/stable/userguide/optimizing.html#prefetch-limits
|
|
902
|
+
CELERY_WORKER_PREFETCH_MULTIPLIER = int(os.getenv("NAUTOBOT_CELERY_WORKER_PREFETCH_MULTIPLIER", "4"))
|
|
903
|
+
|
|
895
904
|
# If enabled stdout and stderr of running jobs will be redirected to the task logger.
|
|
896
905
|
CELERY_WORKER_REDIRECT_STDOUTS = is_truthy(os.getenv("NAUTOBOT_CELERY_WORKER_REDIRECT_STDOUTS", "True"))
|
|
897
906
|
|
|
898
|
-
# The log level of log messages generated by redirected job stdout and stderr.
|
|
907
|
+
# The log level of log messages generated by redirected job stdout and stderr.
|
|
908
|
+
# Can be one of `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL`.
|
|
899
909
|
CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = os.getenv("NAUTOBOT_CELERY_WORKER_REDIRECT_STDOUTS_LEVEL", "WARNING")
|
|
900
910
|
|
|
901
|
-
# Send task-related events so that tasks can be monitored using tools like flower.
|
|
911
|
+
# Send task-related events so that tasks can be monitored using tools like flower.
|
|
912
|
+
# Sets the default value for the workers -E argument.
|
|
902
913
|
CELERY_WORKER_SEND_TASK_EVENTS = True
|
|
903
914
|
|
|
904
915
|
# Default celery queue name that will be used by workers and tasks if no queue is specified
|
nautobot/core/settings.yaml
CHANGED
|
@@ -426,6 +426,20 @@ properties:
|
|
|
426
426
|
see_also:
|
|
427
427
|
"`CELERY_TASK_SOFT_TIME_LIMIT`": "#celery_task_soft_time_limit"
|
|
428
428
|
type: "integer"
|
|
429
|
+
CELERY_WORKER_PREFETCH_MULTIPLIER:
|
|
430
|
+
default: 4
|
|
431
|
+
description: "How many tasks a worker is allowed to reserve for its own consumption and execution."
|
|
432
|
+
details: >-
|
|
433
|
+
If set to `0` (not recommended) a single worker can reserve all tasks even if other workers are free.
|
|
434
|
+
For short running tasks (such as webhooks) you may want to set this to a larger number to increase throughput.
|
|
435
|
+
Conversely, for long-running tasks (such as SSoT or Golden-Config Jobs at scale) you may want to set this to `1`
|
|
436
|
+
so that a worker executing a long-running task will not prefetch other tasks, which would block their execution
|
|
437
|
+
until the long-running task completes.
|
|
438
|
+
environment_variable: "NAUTOBOT_CELERY_WORKER_PREFETCH_MULTIPLIER"
|
|
439
|
+
see_also:
|
|
440
|
+
"Celery documentation": "https://docs.celeryq.dev/en/stable/userguide/optimizing.html#prefetch-limits"
|
|
441
|
+
type: "integer"
|
|
442
|
+
version_added: "2.2.9"
|
|
429
443
|
CELERY_WORKER_PROMETHEUS_PORTS:
|
|
430
444
|
default: []
|
|
431
445
|
description: "Ports for Prometheus metric HTTP server running on the celery worker(s)."
|
|
@@ -468,8 +482,8 @@ properties:
|
|
|
468
482
|
If enabling indefinite changelog retention, it is recommended to periodically delete old entries.
|
|
469
483
|
Otherwise, the database may eventually exceed capacity.
|
|
470
484
|
|
|
471
|
-
+/- 2.
|
|
472
|
-
As of Nautobot 2.
|
|
485
|
+
+/- 2.3.0
|
|
486
|
+
As of Nautobot 2.3.0, changelog cleanup does not run automatically. Use the `Cleanup of ObjectChange records`
|
|
473
487
|
system Job to handle changelog cleanup; you may schedule this to run automatically like any other Job if
|
|
474
488
|
desired. The `CHANGELOG_RETENTION` setting provides a default age cutoff for the Job but may be overridden
|
|
475
489
|
at runtime if desired.
|
|
@@ -1816,9 +1830,9 @@ properties:
|
|
|
1816
1830
|
The time zone Nautobot will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. Please see the [list of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
|
|
1817
1831
|
details: |-
|
|
1818
1832
|
!!! warning
|
|
1819
|
-
Scheduled jobs will
|
|
1820
|
-
default UTC,
|
|
1821
|
-
jobs may run in the wrong time zone.
|
|
1833
|
+
Scheduled jobs will default to running in the time zone configured in this setting.
|
|
1834
|
+
If you change this setting from the default UTC, it must be set consistently on the Celery Beat server
|
|
1835
|
+
and all Nautobot web servers, or else your scheduled jobs may run in the wrong time zone.
|
|
1822
1836
|
environment_variable: "NAUTOBOT_TIME_ZONE"
|
|
1823
1837
|
see_also:
|
|
1824
1838
|
"Time Zones documentation": "./time-zones.md"
|
nautobot/core/tables.py
CHANGED
|
@@ -49,7 +49,7 @@ class BaseTable(django_tables2.Table):
|
|
|
49
49
|
# Add custom field columns
|
|
50
50
|
model = self._meta.model
|
|
51
51
|
|
|
52
|
-
if model
|
|
52
|
+
if getattr(model, "is_dynamic_group_associable_model", False):
|
|
53
53
|
self.base_columns["dynamic_group_count"] = LinkedCountColumn(
|
|
54
54
|
viewname="extras:dynamicgroup_list",
|
|
55
55
|
url_params={"member_id": "pk"},
|
|
@@ -155,6 +155,9 @@ class BaseTable(django_tables2.Table):
|
|
|
155
155
|
continue
|
|
156
156
|
if isinstance(column.column, LinkedCountColumn):
|
|
157
157
|
column_model = lookup.get_model_for_view_name(column.column.viewname)
|
|
158
|
+
if column_model is None:
|
|
159
|
+
logger.error("Couldn't find model for %s", column.column.viewname)
|
|
160
|
+
continue
|
|
158
161
|
reverse_lookup = column.column.reverse_lookup or next(iter(column.column.url_params.keys()))
|
|
159
162
|
count_fields.append((column.name, column_model, reverse_lookup))
|
|
160
163
|
continue
|
|
@@ -106,10 +106,10 @@
|
|
|
106
106
|
{% endwith %}
|
|
107
107
|
{% endif %}
|
|
108
108
|
{% if object.is_metadata_associable_model and perms.extras.view_objectmetadata %}
|
|
109
|
-
{% with object.
|
|
109
|
+
{% with object.associated_object_metadata.count as om_count %}
|
|
110
110
|
{% if om_count %}
|
|
111
|
-
<li role="presentation"{% if request.GET.tab == '
|
|
112
|
-
<a href="{{ object.get_absolute_url }}#
|
|
111
|
+
<li role="presentation"{% if request.GET.tab == 'object_metadata' %} class="active"{% endif %}>
|
|
112
|
+
<a href="{{ object.get_absolute_url }}#object_metadata" onclick="switch_tab(this.href)" aria-controls="object_metadata" role="tab" data-toggle="tab">
|
|
113
113
|
Object Metadata {% badge om_count %}
|
|
114
114
|
</a>
|
|
115
115
|
</li>
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
<div class="row">
|
|
135
135
|
<div class="col-md-6">
|
|
136
136
|
{% block content_left_page %}{% endblock content_left_page %}
|
|
137
|
-
{% include 'inc/custom_fields/panel.html' with custom_fields=object.get_custom_field_groupings_basic computed_fields_advanced_ui=False %}
|
|
137
|
+
{% include 'inc/custom_fields/panel.html' with custom_fields=object.get_custom_field_groupings_basic computed_fields=object.get_computed_fields_grouping_basic computed_fields_advanced_ui=False %}
|
|
138
138
|
{% include 'inc/relationships_panel.html' %}
|
|
139
139
|
{% include 'extras/inc/tags_panel.html' %}
|
|
140
140
|
{% plugin_left_page object %}
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
</div>
|
|
247
247
|
{% endif %}
|
|
248
248
|
{% if object.is_metadata_associable_model and perms.extras.view_objectmetadata %}
|
|
249
|
-
<div id="
|
|
249
|
+
<div id="object_metadata" role="tabpanel" class="tab-pane {% if request.GET.tab == 'object_metadata' %}active{% else %}fade{% endif %}">
|
|
250
250
|
<div class="row">
|
|
251
251
|
<div class="col-md-12">
|
|
252
252
|
<form method="post">
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
</div>
|
|
260
260
|
</div>
|
|
261
261
|
<div class="table-responsive">
|
|
262
|
-
{% render_table
|
|
262
|
+
{% render_table associated_object_metadata_table 'inc/table.html' %}
|
|
263
263
|
</div>
|
|
264
264
|
</div>
|
|
265
265
|
</form>
|
|
@@ -39,10 +39,11 @@
|
|
|
39
39
|
{% for panel_name, panel_details in registry.homepage_layout.panels.items %}
|
|
40
40
|
{% if request.user|has_one_or_more_perms:panel_details.permissions %}
|
|
41
41
|
<div class="panel panel-default" id="{{ panel_name|slugify }}" style="break-inside: avoid" data-panel-weight="{{ panel_details.weight }}">
|
|
42
|
-
<div class="panel-heading">
|
|
43
|
-
<strong>{{ panel_name }}</strong><span id="toggle-homepanel-{{ panel_name|slugify }}" class="glyphicon glyphicon-chevron-down collapse-icon" type="button" data-toggle="collapse" data-target="#homepanel-{{ panel_name|slugify }}" aria-expanded="false" aria-controls="homepanel-{{ panel_name|slugify }}"></span>
|
|
44
|
-
</div>
|
|
45
42
|
{% with cookie_key='homepanel-'|add:panel_name|slugify %}
|
|
43
|
+
<div class="panel-heading">
|
|
44
|
+
<strong>{{ panel_name }}</strong>
|
|
45
|
+
<span id="collapse-icon-{{ panel_name|slugify }}" class="glyphicon glyphicon-chevron-down collapse-icon{% if request.COOKIES|default:''|get_item:cookie_key|default:'False' == 'False' %} rotated180{% endif %}" type="button" data-toggle="collapse" data-target="#homepanel-{{ panel_name|slugify }}" aria-expanded="false" aria-controls="homepanel-{{ panel_name|slugify }}"></span>
|
|
46
|
+
</div>
|
|
46
47
|
<div class="list-group collapse{% if request.COOKIES|default:''|get_item:cookie_key|default:'False' == 'False' %} in{% endif %} collapsible-div" id="homepanel-{{ panel_name|slugify }}" >
|
|
47
48
|
{% endwith %}
|
|
48
49
|
{% if panel_details.rendered_html %}
|