nautobot 2.3.0__py3-none-any.whl → 2.3.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/cloud/factory.py +0 -2
- nautobot/cloud/filters.py +0 -3
- nautobot/cloud/forms.py +1 -7
- nautobot/cloud/migrations/0001_initial.py +1 -1
- nautobot/cloud/models.py +2 -1
- nautobot/cloud/tables.py +17 -1
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +7 -1
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -11
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -4
- nautobot/cloud/tests/test_filters.py +0 -12
- nautobot/core/filters.py +1 -15
- nautobot/core/forms/forms.py +2 -10
- nautobot/core/graphql/generators.py +2 -2
- nautobot/core/graphql/schema.py +14 -6
- nautobot/core/jobs/__init__.py +1 -4
- nautobot/core/management/commands/generate_test_data.py +2 -2
- nautobot/core/models/__init__.py +2 -2
- nautobot/core/settings.py +2 -13
- nautobot/core/settings.yaml +2 -16
- nautobot/core/tables.py +0 -3
- nautobot/core/templates/generic/object_retrieve.html +5 -5
- nautobot/core/templates/nautobot_config.py.j2 +0 -15
- nautobot/core/testing/filters.py +1 -12
- nautobot/core/tests/integration/test_general_functionality.py +1 -1
- nautobot/core/tests/test_jobs.py +1 -74
- nautobot/core/views/generic.py +1 -1
- nautobot/core/views/mixins.py +1 -1
- nautobot/core/views/utils.py +6 -8
- nautobot/dcim/factory.py +1 -4
- nautobot/dcim/filters/__init__.py +0 -4
- nautobot/dcim/forms.py +0 -5
- nautobot/dcim/migrations/0061_module_models.py +0 -1
- nautobot/dcim/models/device_components.py +0 -7
- nautobot/dcim/models/devices.py +4 -6
- nautobot/dcim/models/racks.py +1 -0
- nautobot/dcim/tables/devices.py +3 -17
- nautobot/dcim/tables/devicetypes.py +1 -1
- nautobot/dcim/templates/dcim/device/base.html +1 -1
- nautobot/dcim/templates/dcim/device.html +2 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -6
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +0 -17
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +2 -2
- nautobot/dcim/tests/test_api.py +0 -2
- nautobot/dcim/tests/test_filters.py +7 -14
- nautobot/dcim/tests/test_models.py +0 -31
- nautobot/dcim/tests/test_views.py +0 -39
- nautobot/dcim/views.py +1 -4
- nautobot/extras/api/views.py +59 -7
- nautobot/extras/factory.py +12 -50
- nautobot/extras/forms/base.py +4 -10
- nautobot/extras/homepage.py +2 -12
- nautobot/extras/jobs.py +2 -2
- nautobot/extras/migrations/0111_metadata.py +4 -4
- nautobot/extras/models/jobs.py +0 -83
- nautobot/extras/models/metadata.py +18 -18
- nautobot/extras/models/models.py +0 -2
- nautobot/extras/signals.py +1 -14
- nautobot/extras/tables.py +14 -43
- nautobot/extras/templates/extras/job_detail.html +0 -11
- nautobot/extras/tests/test_api.py +9 -16
- nautobot/extras/tests/test_jobs.py +2 -2
- nautobot/extras/tests/test_models.py +18 -20
- nautobot/extras/tests/test_views.py +3 -23
- nautobot/extras/utils.py +6 -35
- nautobot/extras/views.py +50 -27
- nautobot/ipam/filters.py +1 -1
- nautobot/ipam/forms.py +1 -1
- nautobot/ipam/models.py +20 -9
- nautobot/ipam/tables.py +0 -4
- nautobot/ipam/tests/test_models.py +2 -3
- nautobot/ipam/views.py +11 -6
- nautobot/project-static/css/base.css +0 -1
- 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.76a95c52.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.76a95c52.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 +18 -66
- 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 +18 -66
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +18 -34
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +18 -82
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +21 -75
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +18 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +18 -34
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +18 -34
- 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 +19 -21
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +18 -34
- 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 +22 -33
- 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 +18 -29
- nautobot/project-static/docs/development/core/model-checklist.html +20 -26
- 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 +379 -393
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +18 -18
- nautobot/project-static/docs/index.html +13 -9032
- 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 +9032 -13
- nautobot/project-static/docs/release-notes/index.html +19 -252
- 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 +111 -248
- nautobot/project-static/docs/release-notes/version-2.3.html +90 -520
- 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 +20 -52
- 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 +18 -22
- 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 +82 -69
- nautobot/project-static/docs/user-guide/administration/installation/index.html +24 -24
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +52 -60
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +87 -80
- nautobot/project-static/docs/user-guide/administration/installation/services.html +44 -37
- 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 +24 -76
- 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 +18 -62
- 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/{objectmetadata.html → metadata.html} +84 -197
- 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/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/tenancy/templates/tenancy/tenant.html +4 -4
- nautobot/virtualization/models.py +2 -0
- nautobot/virtualization/tables.py +5 -2
- {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/METADATA +3 -3
- {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/RECORD +364 -364
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +0 -1
- nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +0 -1
- {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/NOTICE +0 -0
- {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/WHEEL +0 -0
- {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/entry_points.txt +0 -0
nautobot/extras/factory.py
CHANGED
|
@@ -251,24 +251,6 @@ 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
|
-
|
|
272
254
|
class ObjectMetadataFactory(BaseModelFactory):
|
|
273
255
|
"""ObjectMetadata model factory"""
|
|
274
256
|
|
|
@@ -281,6 +263,7 @@ class ObjectMetadataFactory(BaseModelFactory):
|
|
|
281
263
|
MetadataType.objects.all(),
|
|
282
264
|
allow_null=False,
|
|
283
265
|
)
|
|
266
|
+
scoped_fields = factory.Faker("pylist", allowed_types=[str])
|
|
284
267
|
|
|
285
268
|
@factory.lazy_attribute
|
|
286
269
|
def contact(self):
|
|
@@ -325,43 +308,22 @@ class ObjectMetadataFactory(BaseModelFactory):
|
|
|
325
308
|
raise RuntimeError(f"Unsupported metadatatype datatype {metadata_type_data_type}")
|
|
326
309
|
|
|
327
310
|
@factory.lazy_attribute
|
|
328
|
-
def
|
|
329
|
-
|
|
330
|
-
|
|
311
|
+
def assigned_object_type(self):
|
|
312
|
+
while True:
|
|
313
|
+
allowed_content_types = list(self.metadata_type.content_types.values_list("pk", flat=True))
|
|
314
|
+
content_type = factory.random.randgen.choice(
|
|
315
|
+
ContentType.objects.filter(FeatureQuery("metadata").get_query(), pk__in=allowed_content_types)
|
|
316
|
+
)
|
|
331
317
|
# It does not have a get_absolute_url attribute and is causing failure in API unittests
|
|
332
318
|
if content_type.app_label == "extras" and content_type.model == "taggeditem":
|
|
333
319
|
continue
|
|
334
|
-
|
|
335
|
-
|
|
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}")
|
|
320
|
+
if content_type.model_class().objects.exists():
|
|
321
|
+
return content_type
|
|
347
322
|
|
|
348
323
|
@factory.lazy_attribute
|
|
349
|
-
def
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
)
|
|
324
|
+
def assigned_object_id(self):
|
|
325
|
+
queryset = self.assigned_object_type.model_class().objects.all()
|
|
326
|
+
return factory.random.randgen.choice(queryset).pk
|
|
365
327
|
|
|
366
328
|
|
|
367
329
|
class ObjectChangeFactory(BaseModelFactory):
|
nautobot/extras/forms/base.py
CHANGED
|
@@ -26,12 +26,11 @@ __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
|
|
31
29
|
CustomFieldModelFormMixin,
|
|
32
30
|
DynamicGroupModelFormMixin,
|
|
33
31
|
NoteModelFormMixin,
|
|
34
32
|
RelationshipModelFormMixin,
|
|
33
|
+
BootstrapMixin,
|
|
35
34
|
):
|
|
36
35
|
"""
|
|
37
36
|
This class exists to combine common functionality and is used to inherit from throughout the
|
|
@@ -41,10 +40,9 @@ class NautobotModelForm(
|
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
class NautobotFilterForm(
|
|
44
|
-
BootstrapMixin,
|
|
45
|
-
# The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
|
|
46
43
|
ContactTeamModelFilterFormMixin,
|
|
47
|
-
|
|
44
|
+
BootstrapMixin,
|
|
45
|
+
CustomFieldModelFilterFormMixin, # currently must come *after* BootstrapMixin to get proper CSS classes applied
|
|
48
46
|
RelationshipModelFilterFormMixin,
|
|
49
47
|
):
|
|
50
48
|
"""
|
|
@@ -55,10 +53,6 @@ class NautobotFilterForm(
|
|
|
55
53
|
|
|
56
54
|
|
|
57
55
|
class NautobotBulkEditForm(
|
|
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,
|
|
56
|
+
BootstrapMixin, CustomFieldModelBulkEditFormMixin, RelationshipModelBulkEditFormMixin, NoteModelBulkEditFormMixin
|
|
63
57
|
):
|
|
64
58
|
"""Base class for bulk-edit forms for models that support relationships, custom fields and notes."""
|
nautobot/extras/homepage.py
CHANGED
|
@@ -7,24 +7,14 @@ 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
|
-
.
|
|
11
|
-
.only("id", "name", "status", "date_done", "user")
|
|
10
|
+
.defer("result")
|
|
12
11
|
.order_by("-date_done")[:10]
|
|
13
12
|
)
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def get_changelog(request):
|
|
17
16
|
"""Callback function to collect changelog for panel."""
|
|
18
|
-
return ObjectChange.objects.restrict(request.user, "view")
|
|
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]
|
|
17
|
+
return ObjectChange.objects.restrict(request.user, "view")[:15]
|
|
28
18
|
|
|
29
19
|
|
|
30
20
|
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 InMemoryUploadedFile
|
|
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, InMemoryUploadedFile):
|
|
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):
|
|
@@ -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)),
|
|
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_metadatas",
|
|
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_metadatas",
|
|
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_metadatas",
|
|
113
113
|
to="extras.team",
|
|
114
114
|
),
|
|
115
115
|
),
|
nautobot/extras/models/jobs.py
CHANGED
|
@@ -456,8 +456,6 @@ class JobLogEntry(BaseModel):
|
|
|
456
456
|
log_object = models.CharField(max_length=JOB_LOG_MAX_LOG_OBJECT_LENGTH, blank=True, default="")
|
|
457
457
|
absolute_url = models.CharField(max_length=JOB_LOG_MAX_ABSOLUTE_URL_LENGTH, blank=True, default="")
|
|
458
458
|
|
|
459
|
-
is_metadata_associable_model = False
|
|
460
|
-
|
|
461
459
|
documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
|
|
462
460
|
|
|
463
461
|
def __str__(self):
|
|
@@ -1077,87 +1075,6 @@ class ScheduledJob(BaseModel):
|
|
|
1077
1075
|
day_of_week=day_of_week,
|
|
1078
1076
|
)
|
|
1079
1077
|
|
|
1080
|
-
@classmethod
|
|
1081
|
-
def create_schedule(
|
|
1082
|
-
cls,
|
|
1083
|
-
job_model,
|
|
1084
|
-
user,
|
|
1085
|
-
name=None,
|
|
1086
|
-
start_time=None,
|
|
1087
|
-
interval=JobExecutionType.TYPE_IMMEDIATELY,
|
|
1088
|
-
crontab="",
|
|
1089
|
-
profile=False,
|
|
1090
|
-
approval_required=False,
|
|
1091
|
-
task_queue=None,
|
|
1092
|
-
**job_kwargs,
|
|
1093
|
-
):
|
|
1094
|
-
"""
|
|
1095
|
-
Schedule a job with the specified parameters.
|
|
1096
|
-
|
|
1097
|
-
This method creates a schedule for a job to be executed at a specific time
|
|
1098
|
-
or interval. It handles immediate execution, custom start times, and
|
|
1099
|
-
crontab-based scheduling.
|
|
1100
|
-
|
|
1101
|
-
Parameters:
|
|
1102
|
-
job_model (JobModel): The job model instance.
|
|
1103
|
-
user (User): The user who is scheduling the job.
|
|
1104
|
-
name (str, optional): The name of the scheduled job. Defaults to None.
|
|
1105
|
-
start_time (datetime, optional): The start time for the job. Defaults to None.
|
|
1106
|
-
interval (JobExecutionType, optional): The interval type for the job execution.
|
|
1107
|
-
Defaults to JobExecutionType.TYPE_IMMEDIATELY.
|
|
1108
|
-
crontab (str, optional): The crontab string for the schedule. Defaults to "".
|
|
1109
|
-
profile (bool, optional): Flag indicating whether to profile the job. Defaults to False.
|
|
1110
|
-
approval_required (bool, optional): Flag indicating if approval is required. Defaults to False.
|
|
1111
|
-
task_queue (str, optional): The task queue for the job. Defaults to None, which will use the configured default celery queue.
|
|
1112
|
-
**job_kwargs: Additional keyword arguments to pass to the job.
|
|
1113
|
-
|
|
1114
|
-
Returns:
|
|
1115
|
-
ScheduledJob instance
|
|
1116
|
-
"""
|
|
1117
|
-
|
|
1118
|
-
if interval == JobExecutionType.TYPE_IMMEDIATELY:
|
|
1119
|
-
start_time = timezone.now()
|
|
1120
|
-
name = name or f"{job_model.name} - {start_time}"
|
|
1121
|
-
elif interval == JobExecutionType.TYPE_CUSTOM:
|
|
1122
|
-
if start_time is None:
|
|
1123
|
-
# "start_time" is checked against models.ScheduledJob.earliest_possible_time()
|
|
1124
|
-
# which returns timezone.now() + timedelta(seconds=15)
|
|
1125
|
-
start_time = timezone.now() + timedelta(seconds=20)
|
|
1126
|
-
|
|
1127
|
-
celery_kwargs = {
|
|
1128
|
-
"nautobot_job_profile": profile,
|
|
1129
|
-
"queue": task_queue,
|
|
1130
|
-
}
|
|
1131
|
-
if job_model.soft_time_limit > 0:
|
|
1132
|
-
celery_kwargs["soft_time_limit"] = job_model.soft_time_limit
|
|
1133
|
-
if job_model.time_limit > 0:
|
|
1134
|
-
celery_kwargs["time_limit"] = job_model.time_limit
|
|
1135
|
-
|
|
1136
|
-
# 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
|
|
1137
|
-
#
|
|
1138
|
-
# We pass in task and job_model here partly for forward/backward compatibility logic, and
|
|
1139
|
-
# part fallback safety. It's mildly useful to store both the task module/class name and the JobModel
|
|
1140
|
-
# FK on the ScheduledJob, as in the case where the JobModel gets deleted (and the FK becomes
|
|
1141
|
-
# null) you still have a bit of context on the ScheduledJob as to what it was originally
|
|
1142
|
-
# scheduled for.
|
|
1143
|
-
scheduled_job = cls(
|
|
1144
|
-
name=name,
|
|
1145
|
-
task=job_model.class_path,
|
|
1146
|
-
job_model=job_model,
|
|
1147
|
-
start_time=start_time,
|
|
1148
|
-
description=f"Nautobot job {name} scheduled by {user} for {start_time}",
|
|
1149
|
-
kwargs=job_kwargs,
|
|
1150
|
-
celery_kwargs=celery_kwargs,
|
|
1151
|
-
interval=interval,
|
|
1152
|
-
one_off=(interval == JobExecutionType.TYPE_FUTURE),
|
|
1153
|
-
user=user,
|
|
1154
|
-
approval_required=approval_required,
|
|
1155
|
-
crontab=crontab,
|
|
1156
|
-
queue=task_queue,
|
|
1157
|
-
)
|
|
1158
|
-
scheduled_job.validated_save()
|
|
1159
|
-
return scheduled_job
|
|
1160
|
-
|
|
1161
1078
|
def to_cron(self):
|
|
1162
1079
|
t = self.start_time
|
|
1163
1080
|
if self.interval == JobExecutionType.TYPE_HOURLY:
|
|
@@ -61,7 +61,7 @@ class MetadataType(PrimaryModel):
|
|
|
61
61
|
|
|
62
62
|
objects = MetadataTypeManager()
|
|
63
63
|
clone_fields = ["data_type"]
|
|
64
|
-
documentation_static_path = "docs/user-guide/platform-functionality/
|
|
64
|
+
documentation_static_path = "docs/user-guide/platform-functionality/metadata.html"
|
|
65
65
|
|
|
66
66
|
class Meta:
|
|
67
67
|
ordering = ["name"]
|
|
@@ -106,7 +106,7 @@ class MetadataChoice(ChangeLoggedModel, BaseModel):
|
|
|
106
106
|
weight = models.PositiveSmallIntegerField(default=100, help_text="Higher weights appear later in the list")
|
|
107
107
|
is_metadata_associable_model = False
|
|
108
108
|
|
|
109
|
-
documentation_static_path = "docs/user-guide/platform-functionality/
|
|
109
|
+
documentation_static_path = "docs/user-guide/platform-functionality/metadata.html"
|
|
110
110
|
|
|
111
111
|
class Meta:
|
|
112
112
|
ordering = ["metadata_type", "weight", "value"]
|
|
@@ -166,19 +166,19 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
166
166
|
metadata_type = models.ForeignKey(
|
|
167
167
|
to=MetadataType,
|
|
168
168
|
on_delete=models.PROTECT,
|
|
169
|
-
related_name="
|
|
169
|
+
related_name="object_metadatas",
|
|
170
170
|
)
|
|
171
171
|
contact = models.ForeignKey(
|
|
172
172
|
to=Contact,
|
|
173
173
|
on_delete=models.PROTECT,
|
|
174
|
-
related_name="
|
|
174
|
+
related_name="object_metadatas",
|
|
175
175
|
blank=True,
|
|
176
176
|
null=True,
|
|
177
177
|
)
|
|
178
178
|
team = models.ForeignKey(
|
|
179
179
|
to=Team,
|
|
180
180
|
on_delete=models.PROTECT,
|
|
181
|
-
related_name="
|
|
181
|
+
related_name="object_metadatas",
|
|
182
182
|
blank=True,
|
|
183
183
|
null=True,
|
|
184
184
|
)
|
|
@@ -186,7 +186,6 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
186
186
|
base_field=models.CharField(
|
|
187
187
|
max_length=CHARFIELD_MAX_LENGTH,
|
|
188
188
|
),
|
|
189
|
-
blank=True,
|
|
190
189
|
help_text="List of scoped fields, only direct fields on the model",
|
|
191
190
|
)
|
|
192
191
|
_value = models.JSONField(
|
|
@@ -200,7 +199,7 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
200
199
|
|
|
201
200
|
objects = ObjectMetadataManager()
|
|
202
201
|
natural_key_field_names = ["pk"]
|
|
203
|
-
documentation_static_path = "docs/user-guide/platform-functionality/
|
|
202
|
+
documentation_static_path = "docs/user-guide/platform-functionality/metadata.html"
|
|
204
203
|
|
|
205
204
|
class Meta:
|
|
206
205
|
ordering = ["metadata_type"]
|
|
@@ -362,17 +361,12 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
362
361
|
raise ValidationError("Date values must be in the format YYYY-MM-DD.")
|
|
363
362
|
# Validate datetime
|
|
364
363
|
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_DATETIME:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
acceptable_datetime_formats = [
|
|
372
|
-
"YYYY-MM-DDTHH:MM:SS",
|
|
373
|
-
"YYYY-MM-DDTHH:MM:SS(+,-)zzzz",
|
|
374
|
-
"YYYY-MM-DDTHH:MM:SS(+,-)zz:zz",
|
|
375
|
-
]
|
|
364
|
+
acceptable_datetime_formats = [
|
|
365
|
+
"YYYY-MM-DDTHH:MM:SS",
|
|
366
|
+
"YYYY-MM-DDTHH:MM:SS(+,-)zzzz",
|
|
367
|
+
"YYYY-MM-DDTHH:MM:SS(+,-)zz:zz",
|
|
368
|
+
]
|
|
369
|
+
if not isinstance(value, datetime):
|
|
376
370
|
try:
|
|
377
371
|
datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z")
|
|
378
372
|
except ValueError:
|
|
@@ -386,6 +380,12 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
|
|
|
386
380
|
)
|
|
387
381
|
except TypeError:
|
|
388
382
|
raise ValidationError("Value must be a datetime or str object.")
|
|
383
|
+
else:
|
|
384
|
+
# if value is a datetime object
|
|
385
|
+
# check if datetime object has tzinfo
|
|
386
|
+
if value.tzinfo is None:
|
|
387
|
+
value = value.replace(tzinfo=timezone.utc)
|
|
388
|
+
value = value.replace(microsecond=0).isoformat()
|
|
389
389
|
# Validate selected choice
|
|
390
390
|
elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_SELECT:
|
|
391
391
|
if value not in self.metadata_type.choices.values_list("value", flat=True):
|
nautobot/extras/models/models.py
CHANGED
|
@@ -822,8 +822,6 @@ class Note(ChangeLoggedModel, BaseModel):
|
|
|
822
822
|
note = models.TextField()
|
|
823
823
|
objects = BaseManager.from_queryset(NotesQuerySet)()
|
|
824
824
|
|
|
825
|
-
is_metadata_associable_model = False
|
|
826
|
-
|
|
827
825
|
class Meta:
|
|
828
826
|
ordering = ["created"]
|
|
829
827
|
unique_together = [["assigned_object_type", "assigned_object_id", "user_name", "created"]]
|
nautobot/extras/signals.py
CHANGED
|
@@ -13,7 +13,7 @@ from django.core.cache import cache
|
|
|
13
13
|
from django.core.exceptions import ValidationError
|
|
14
14
|
from django.core.files.storage import get_storage_class
|
|
15
15
|
from django.db import transaction
|
|
16
|
-
from django.db.models.signals import m2m_changed, post_delete,
|
|
16
|
+
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete, pre_save
|
|
17
17
|
from django.dispatch import receiver
|
|
18
18
|
from django.utils import timezone
|
|
19
19
|
from django_prometheus.models import model_deletes, model_inserts, model_updates
|
|
@@ -289,19 +289,6 @@ def _handle_deleted_object(sender, instance, **kwargs):
|
|
|
289
289
|
model_deletes.labels(instance._meta.model_name).inc()
|
|
290
290
|
|
|
291
291
|
|
|
292
|
-
#
|
|
293
|
-
# Content types
|
|
294
|
-
#
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
@receiver(post_migrate)
|
|
298
|
-
def post_migrate_clear_content_type_caches(sender, app_config, signal, **kwargs):
|
|
299
|
-
"""Clear various content-type caches after a migration."""
|
|
300
|
-
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
301
|
-
cache.delete("nautobot.extras.utils.change_logged_models_queryset")
|
|
302
|
-
cache.delete_pattern("nautobot.extras.utils.FeatureQuery.*")
|
|
303
|
-
|
|
304
|
-
|
|
305
292
|
#
|
|
306
293
|
# Custom fields
|
|
307
294
|
#
|
nautobot/extras/tables.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
|
-
from django.utils.html import format_html
|
|
2
|
+
from django.utils.html import format_html
|
|
3
3
|
import django_tables2 as tables
|
|
4
4
|
from django_tables2.utils import Accessor
|
|
5
5
|
from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
|
|
@@ -107,7 +107,6 @@ GITREPOSITORY_BUTTONS = """
|
|
|
107
107
|
|
|
108
108
|
JOB_BUTTONS = """
|
|
109
109
|
<a href="{% url 'extras:job' pk=record.pk %}" class="btn btn-default btn-xs" title="Details"><i class="mdi mdi-information-outline" aria-hidden="true"></i></a>
|
|
110
|
-
<a href="{% url 'extras:jobresult_list' %}?job_model={{ record.name | urlencode }}" class="btn btn-default btn-xs" title="Job Results"><i class="mdi mdi-format-list-bulleted" aria-hidden="true"></i></a>
|
|
111
110
|
"""
|
|
112
111
|
|
|
113
112
|
OBJECTCHANGE_OBJECT = """
|
|
@@ -716,10 +715,7 @@ class JobTable(BaseTable):
|
|
|
716
715
|
return render_markdown(value)
|
|
717
716
|
|
|
718
717
|
def render_name(self, value):
|
|
719
|
-
return format_html(
|
|
720
|
-
'<span class="btn btn-primary btn-xs"><i class="mdi mdi-play"></i></span>{}',
|
|
721
|
-
value,
|
|
722
|
-
)
|
|
718
|
+
return format_html('<span class="btn btn-primary btn-xs"><i class="mdi mdi-play"></i></span>{}', value)
|
|
723
719
|
|
|
724
720
|
class Meta(BaseTable.Meta):
|
|
725
721
|
model = JobModel
|
|
@@ -891,16 +887,7 @@ class JobResultTable(BaseTable):
|
|
|
891
887
|
"summary",
|
|
892
888
|
"actions",
|
|
893
889
|
)
|
|
894
|
-
default_columns = (
|
|
895
|
-
"pk",
|
|
896
|
-
"date_created",
|
|
897
|
-
"name",
|
|
898
|
-
"job_model",
|
|
899
|
-
"user",
|
|
900
|
-
"status",
|
|
901
|
-
"summary",
|
|
902
|
-
"actions",
|
|
903
|
-
)
|
|
890
|
+
default_columns = ("pk", "date_created", "name", "job_model", "user", "status", "summary", "actions")
|
|
904
891
|
|
|
905
892
|
|
|
906
893
|
class JobButtonTable(BaseTable):
|
|
@@ -970,8 +957,10 @@ class MetadataTypeTable(BaseTable):
|
|
|
970
957
|
class ObjectMetadataTable(BaseTable):
|
|
971
958
|
pk = ToggleColumn()
|
|
972
959
|
metadata_type = tables.Column(linkify=True)
|
|
973
|
-
assigned_object = tables.TemplateColumn(
|
|
974
|
-
|
|
960
|
+
assigned_object = tables.TemplateColumn(template_code=ASSIGNED_OBJECT, orderable=False)
|
|
961
|
+
actions = ButtonsColumn(
|
|
962
|
+
ObjectMetadata,
|
|
963
|
+
buttons=("delete"),
|
|
975
964
|
)
|
|
976
965
|
# This is needed so that render_value method below does not skip itself
|
|
977
966
|
# when metadata_type.data_type is TYPE_CONTACT_TEAM and we need it to display either contact or team
|
|
@@ -985,6 +974,7 @@ class ObjectMetadataTable(BaseTable):
|
|
|
985
974
|
"metadata_type",
|
|
986
975
|
"scoped_fields",
|
|
987
976
|
"value",
|
|
977
|
+
"actions",
|
|
988
978
|
)
|
|
989
979
|
default_columns = (
|
|
990
980
|
"pk",
|
|
@@ -992,12 +982,11 @@ class ObjectMetadataTable(BaseTable):
|
|
|
992
982
|
"scoped_fields",
|
|
993
983
|
"value",
|
|
994
984
|
"metadata_type",
|
|
985
|
+
"actions",
|
|
995
986
|
)
|
|
996
987
|
|
|
997
|
-
def render_scoped_fields(self,
|
|
998
|
-
|
|
999
|
-
return "(all fields)"
|
|
1000
|
-
return format_html_join(", ", "<code>{}</code>", ([v] for v in sorted(value)))
|
|
988
|
+
def render_scoped_fields(self, record):
|
|
989
|
+
return render_json(record.scoped_fields, pretty_print=True)
|
|
1001
990
|
|
|
1002
991
|
def render_value(self, record):
|
|
1003
992
|
if record.value is not None and record.metadata_type.data_type == MetadataTypeDataTypeChoices.TYPE_JSON:
|
|
@@ -1119,15 +1108,7 @@ class RelationshipAssociationTable(BaseTable):
|
|
|
1119
1108
|
|
|
1120
1109
|
class Meta(BaseTable.Meta):
|
|
1121
1110
|
model = RelationshipAssociation
|
|
1122
|
-
fields = (
|
|
1123
|
-
"pk",
|
|
1124
|
-
"relationship",
|
|
1125
|
-
"source_type",
|
|
1126
|
-
"source",
|
|
1127
|
-
"destination_type",
|
|
1128
|
-
"destination",
|
|
1129
|
-
"actions",
|
|
1130
|
-
)
|
|
1111
|
+
fields = ("pk", "relationship", "source_type", "source", "destination_type", "destination", "actions")
|
|
1131
1112
|
default_columns = ("pk", "relationship", "source", "destination", "actions")
|
|
1132
1113
|
|
|
1133
1114
|
|
|
@@ -1243,15 +1224,7 @@ class TagTable(BaseTable):
|
|
|
1243
1224
|
|
|
1244
1225
|
class Meta(BaseTable.Meta):
|
|
1245
1226
|
model = Tag
|
|
1246
|
-
fields = (
|
|
1247
|
-
"pk",
|
|
1248
|
-
"name",
|
|
1249
|
-
"items",
|
|
1250
|
-
"color",
|
|
1251
|
-
"content_types",
|
|
1252
|
-
"description",
|
|
1253
|
-
"actions",
|
|
1254
|
-
)
|
|
1227
|
+
fields = ("pk", "name", "items", "color", "content_types", "description", "actions")
|
|
1255
1228
|
|
|
1256
1229
|
|
|
1257
1230
|
class TaggedItemTable(BaseTable):
|
|
@@ -1331,9 +1304,7 @@ class WebhookTable(BaseTable):
|
|
|
1331
1304
|
class AssociatedContactsTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
1332
1305
|
pk = ToggleColumn()
|
|
1333
1306
|
contact_type = tables.TemplateColumn(
|
|
1334
|
-
CONTACT_OR_TEAM_ICON,
|
|
1335
|
-
verbose_name="Type",
|
|
1336
|
-
attrs={"td": {"style": "width:20px;"}},
|
|
1307
|
+
CONTACT_OR_TEAM_ICON, verbose_name="Type", attrs={"td": {"style": "width:20px;"}}
|
|
1337
1308
|
)
|
|
1338
1309
|
name = tables.TemplateColumn(CONTACT_OR_TEAM, verbose_name="Name")
|
|
1339
1310
|
contact_or_team_phone = tables.TemplateColumn(PHONE, accessor="contact_or_team.phone", verbose_name="Phone")
|
|
@@ -115,17 +115,6 @@
|
|
|
115
115
|
<td>{{ object.enabled | render_boolean }}</td>
|
|
116
116
|
<td></td>
|
|
117
117
|
</tr>
|
|
118
|
-
<tr>
|
|
119
|
-
<td>Job Results</td>
|
|
120
|
-
<td>
|
|
121
|
-
{% if object.job_results.exists %}
|
|
122
|
-
<a href="{% url 'extras:jobresult_list' %}?job_model={{ object.name | urlencode }}">{{ object.job_results.count }}</a>
|
|
123
|
-
{% else %}
|
|
124
|
-
{{ None|placeholder }}
|
|
125
|
-
{% endif %}
|
|
126
|
-
</td>
|
|
127
|
-
<td></td>
|
|
128
|
-
</tr>
|
|
129
118
|
</table>
|
|
130
119
|
</div>
|
|
131
120
|
{% endblock content_left_page %}
|
|
@@ -15,7 +15,7 @@ from rest_framework import status
|
|
|
15
15
|
from nautobot.core.choices import ColorChoices
|
|
16
16
|
from nautobot.core.models.fields import slugify_dashes_to_underscores
|
|
17
17
|
from nautobot.core.testing import APITestCase, APIViewTestCases
|
|
18
|
-
from nautobot.core.testing.utils import disable_warnings
|
|
18
|
+
from nautobot.core.testing.utils import disable_warnings
|
|
19
19
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
20
20
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
21
21
|
from nautobot.dcim.models import (
|
|
@@ -2741,21 +2741,21 @@ class ObjectMetadataTest(APIViewTestCases.APIViewTestCase):
|
|
|
2741
2741
|
value="Hey",
|
|
2742
2742
|
scoped_fields=["parent", "status"],
|
|
2743
2743
|
assigned_object_type=ContentType.objects.get_for_model(IPAddress),
|
|
2744
|
-
assigned_object_id=IPAddress.objects.
|
|
2744
|
+
assigned_object_id=IPAddress.objects.first().pk,
|
|
2745
2745
|
)
|
|
2746
2746
|
ObjectMetadata.objects.create(
|
|
2747
2747
|
metadata_type=mdts[0],
|
|
2748
2748
|
value="Hello",
|
|
2749
2749
|
scoped_fields=["namespace"],
|
|
2750
2750
|
assigned_object_type=ContentType.objects.get_for_model(Prefix),
|
|
2751
|
-
assigned_object_id=Prefix.objects.
|
|
2751
|
+
assigned_object_id=Prefix.objects.first().pk,
|
|
2752
2752
|
)
|
|
2753
2753
|
ObjectMetadata.objects.create(
|
|
2754
2754
|
metadata_type=mdts[2],
|
|
2755
2755
|
contact=Contact.objects.first(),
|
|
2756
2756
|
scoped_fields=["status"],
|
|
2757
2757
|
assigned_object_type=ContentType.objects.get_for_model(Prefix),
|
|
2758
|
-
assigned_object_id=Prefix.objects.
|
|
2758
|
+
assigned_object_id=Prefix.objects.last().pk,
|
|
2759
2759
|
)
|
|
2760
2760
|
cls.create_data = [
|
|
2761
2761
|
{
|
|
@@ -2763,41 +2763,34 @@ class ObjectMetadataTest(APIViewTestCases.APIViewTestCase):
|
|
|
2763
2763
|
"scoped_fields": ["location_type"],
|
|
2764
2764
|
"value": "random words",
|
|
2765
2765
|
"assigned_object_type": "dcim.location",
|
|
2766
|
-
"assigned_object_id": Location.objects.
|
|
2766
|
+
"assigned_object_id": Location.objects.first().pk,
|
|
2767
2767
|
},
|
|
2768
2768
|
{
|
|
2769
2769
|
"metadata_type": mdts[1].pk,
|
|
2770
2770
|
"scoped_fields": ["name"],
|
|
2771
2771
|
"value": "random words",
|
|
2772
2772
|
"assigned_object_type": "dcim.location",
|
|
2773
|
-
"assigned_object_id": Location.objects.
|
|
2773
|
+
"assigned_object_id": Location.objects.first().pk,
|
|
2774
2774
|
},
|
|
2775
2775
|
{
|
|
2776
2776
|
"metadata_type": mdts[2].pk,
|
|
2777
|
-
"scoped_fields": [],
|
|
2777
|
+
"scoped_fields": ["device_type"],
|
|
2778
2778
|
"contact": Contact.objects.first().pk,
|
|
2779
2779
|
"assigned_object_type": "dcim.device",
|
|
2780
|
-
"assigned_object_id": Device.objects.
|
|
2780
|
+
"assigned_object_id": Device.objects.first().pk,
|
|
2781
2781
|
},
|
|
2782
2782
|
{
|
|
2783
2783
|
"metadata_type": mdts[2].pk,
|
|
2784
2784
|
"scoped_fields": ["interfaces"],
|
|
2785
2785
|
"team": Team.objects.first().pk,
|
|
2786
2786
|
"assigned_object_type": "dcim.device",
|
|
2787
|
-
"assigned_object_id": Device.objects.
|
|
2787
|
+
"assigned_object_id": Device.objects.first().pk,
|
|
2788
2788
|
},
|
|
2789
2789
|
]
|
|
2790
2790
|
cls.update_data = {
|
|
2791
2791
|
"scoped_fields": ["pk"],
|
|
2792
2792
|
}
|
|
2793
2793
|
|
|
2794
|
-
def get_deletable_object(self):
|
|
2795
|
-
# TODO: CSV round-trip doesn't work for empty scoped_fields values at present. :-(
|
|
2796
|
-
instance = get_deletable_objects(self.model, self._get_queryset().exclude(scoped_fields=[])).first()
|
|
2797
|
-
if instance is None:
|
|
2798
|
-
self.fail("Couldn't find a single deletable object with non-empty scoped_fields")
|
|
2799
|
-
return instance
|
|
2800
|
-
|
|
2801
2794
|
|
|
2802
2795
|
class NoteTest(APIViewTestCases.APIViewTestCase):
|
|
2803
2796
|
model = Note
|