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
|
@@ -55,10 +55,10 @@ class JobTest(TestCase):
|
|
|
55
55
|
|
|
56
56
|
self.assertInHTML(
|
|
57
57
|
"""<tr><th><label for="id_var_int">Var int:</label></th><td>
|
|
58
|
-
<input class="form-control" id="id_var_int" max="3600" name="var_int" placeholder="None" required type="number" value="0">
|
|
58
|
+
<input class="form-control form-control" id="id_var_int" max="3600" name="var_int" placeholder="None" required type="number" value="0">
|
|
59
59
|
<br><span class="helptext">Test default of 0 Falsey</span></td></tr>
|
|
60
60
|
<tr><th><label for="id_var_int_no_default">Var int no default:</label></th><td>
|
|
61
|
-
<input class="form-control" id="id_var_int_no_default" max="3600" name="var_int_no_default" placeholder="None" type="number">
|
|
61
|
+
<input class="form-control form-control" id="id_var_int_no_default" max="3600" name="var_int_no_default" placeholder="None" type="number">
|
|
62
62
|
<br><span class="helptext">Test default without default</span></td></tr>""",
|
|
63
63
|
form.as_table(),
|
|
64
64
|
)
|
|
@@ -1352,7 +1352,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1352
1352
|
value="Invalid assigned object type",
|
|
1353
1353
|
scoped_fields=["status"],
|
|
1354
1354
|
assigned_object_type=ContentType.objects.get_for_model(IPAddress),
|
|
1355
|
-
assigned_object_id=Contact.objects.
|
|
1355
|
+
assigned_object_id=Contact.objects.first().pk,
|
|
1356
1356
|
)
|
|
1357
1357
|
obj_metadata.validated_save()
|
|
1358
1358
|
|
|
@@ -1362,29 +1362,29 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1362
1362
|
)
|
|
1363
1363
|
type_contact_team.content_types.add(ContentType.objects.get_for_model(Contact))
|
|
1364
1364
|
type_contact_team.content_types.add(ContentType.objects.get_for_model(Team))
|
|
1365
|
-
instance1 = ObjectMetadata(
|
|
1365
|
+
instance1 = ObjectMetadata.objects.create(
|
|
1366
1366
|
metadata_type=type_contact_team,
|
|
1367
1367
|
contact=Contact.objects.first(),
|
|
1368
1368
|
team=Team.objects.first(),
|
|
1369
1369
|
scoped_fields=["address"],
|
|
1370
1370
|
assigned_object_type=ContentType.objects.get_for_model(Contact),
|
|
1371
|
-
assigned_object_id=Contact.objects.
|
|
1371
|
+
assigned_object_id=Contact.objects.first().pk,
|
|
1372
1372
|
)
|
|
1373
|
-
instance2 = ObjectMetadata(
|
|
1373
|
+
instance2 = ObjectMetadata.objects.create(
|
|
1374
1374
|
metadata_type=type_contact_team,
|
|
1375
1375
|
contact=None,
|
|
1376
1376
|
team=None,
|
|
1377
1377
|
scoped_fields=["phone"],
|
|
1378
1378
|
assigned_object_type=ContentType.objects.get_for_model(Contact),
|
|
1379
|
-
assigned_object_id=Contact.objects.
|
|
1379
|
+
assigned_object_id=Contact.objects.last().pk,
|
|
1380
1380
|
)
|
|
1381
|
-
instance3 = ObjectMetadata(
|
|
1381
|
+
instance3 = ObjectMetadata.objects.create(
|
|
1382
1382
|
metadata_type=type_contact_team,
|
|
1383
1383
|
contact=Contact.objects.first(),
|
|
1384
1384
|
team=None,
|
|
1385
1385
|
scoped_fields=["email"],
|
|
1386
1386
|
assigned_object_type=ContentType.objects.get_for_model(Team),
|
|
1387
|
-
assigned_object_id=Team.objects.
|
|
1387
|
+
assigned_object_id=Team.objects.first().pk,
|
|
1388
1388
|
)
|
|
1389
1389
|
with self.assertRaises(ValidationError):
|
|
1390
1390
|
instance1.validated_save()
|
|
@@ -1407,7 +1407,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1407
1407
|
value="Some text value",
|
|
1408
1408
|
scoped_fields=["status", "parent"],
|
|
1409
1409
|
assigned_object_type=obj_type,
|
|
1410
|
-
assigned_object_id=Location.objects.
|
|
1410
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1411
1411
|
)
|
|
1412
1412
|
obj_metadata.save()
|
|
1413
1413
|
|
|
@@ -1440,7 +1440,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1440
1440
|
value=15,
|
|
1441
1441
|
scoped_fields=["status", "parent"],
|
|
1442
1442
|
assigned_object_type=obj_type,
|
|
1443
|
-
assigned_object_id=Location.objects.
|
|
1443
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1444
1444
|
)
|
|
1445
1445
|
obj_metadata.validated_save()
|
|
1446
1446
|
|
|
@@ -1484,7 +1484,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1484
1484
|
value=15.245,
|
|
1485
1485
|
scoped_fields=["status", "parent"],
|
|
1486
1486
|
assigned_object_type=obj_type,
|
|
1487
|
-
assigned_object_id=Location.objects.
|
|
1487
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1488
1488
|
)
|
|
1489
1489
|
obj_metadata.validated_save()
|
|
1490
1490
|
|
|
@@ -1525,7 +1525,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1525
1525
|
value=False,
|
|
1526
1526
|
scoped_fields=["status", "parent"],
|
|
1527
1527
|
assigned_object_type=obj_type,
|
|
1528
|
-
assigned_object_id=Location.objects.
|
|
1528
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1529
1529
|
)
|
|
1530
1530
|
obj_metadata.validated_save()
|
|
1531
1531
|
|
|
@@ -1559,7 +1559,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1559
1559
|
value="1994-01-01",
|
|
1560
1560
|
scoped_fields=["status", "parent"],
|
|
1561
1561
|
assigned_object_type=obj_type,
|
|
1562
|
-
assigned_object_id=Location.objects.
|
|
1562
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1563
1563
|
)
|
|
1564
1564
|
obj_metadata.validated_save()
|
|
1565
1565
|
|
|
@@ -1596,7 +1596,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1596
1596
|
value="2024-06-27T17:58:47-0500",
|
|
1597
1597
|
scoped_fields=["status", "parent"],
|
|
1598
1598
|
assigned_object_type=obj_type,
|
|
1599
|
-
assigned_object_id=Location.objects.
|
|
1599
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1600
1600
|
)
|
|
1601
1601
|
obj_metadata.validated_save()
|
|
1602
1602
|
|
|
@@ -1663,7 +1663,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1663
1663
|
value="Option A",
|
|
1664
1664
|
scoped_fields=["status", "parent"],
|
|
1665
1665
|
assigned_object_type=obj_type,
|
|
1666
|
-
assigned_object_id=Location.objects.
|
|
1666
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1667
1667
|
)
|
|
1668
1668
|
obj_metadata.validated_save()
|
|
1669
1669
|
|
|
@@ -1689,7 +1689,7 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1689
1689
|
value=["Option A"],
|
|
1690
1690
|
scoped_fields=["status", "parent"],
|
|
1691
1691
|
assigned_object_type=obj_type,
|
|
1692
|
-
assigned_object_id=Location.objects.
|
|
1692
|
+
assigned_object_id=Location.objects.first().pk,
|
|
1693
1693
|
)
|
|
1694
1694
|
obj_metadata.validated_save()
|
|
1695
1695
|
|
|
@@ -1700,22 +1700,20 @@ class ObjectMetadataTest(ModelTestCases.BaseModelTestCase):
|
|
|
1700
1700
|
self.assertIn(f"Invalid choice(s) ({invalid_options})", str(context.exception))
|
|
1701
1701
|
|
|
1702
1702
|
def test_no_scoped_fields_overlap(self):
|
|
1703
|
-
"""
|
|
1704
|
-
Test that overlapping scoped_fields of ObjectMetadata with same metadata_type/assigned_object is not allowed.
|
|
1705
|
-
"""
|
|
1703
|
+
"""Test that overlapping between scoped_fields of ObjectMetadata with the same metadata_type and the same assigned_object is not allowed"""
|
|
1706
1704
|
ObjectMetadata.objects.create(
|
|
1707
1705
|
metadata_type=MetadataType.objects.first(),
|
|
1708
1706
|
contact=Contact.objects.first(),
|
|
1709
1707
|
scoped_fields=["host", "mask_length", "type", "role", "status"],
|
|
1710
1708
|
assigned_object_type=ContentType.objects.get_for_model(IPAddress),
|
|
1711
|
-
assigned_object_id=IPAddress.objects.
|
|
1709
|
+
assigned_object_id=IPAddress.objects.first().pk,
|
|
1712
1710
|
)
|
|
1713
1711
|
instance2 = ObjectMetadata.objects.create(
|
|
1714
1712
|
metadata_type=MetadataType.objects.first(),
|
|
1715
1713
|
contact=Contact.objects.first(),
|
|
1716
1714
|
scoped_fields=[],
|
|
1717
1715
|
assigned_object_type=ContentType.objects.get_for_model(IPAddress),
|
|
1718
|
-
assigned_object_id=IPAddress.objects.
|
|
1716
|
+
assigned_object_id=IPAddress.objects.first().pk,
|
|
1719
1717
|
)
|
|
1720
1718
|
with self.assertRaises(ValidationError):
|
|
1721
1719
|
# try scope all fields
|
|
@@ -2594,7 +2594,7 @@ class JobTestCase(
|
|
|
2594
2594
|
|
|
2595
2595
|
self.assertInHTML('<option value="uniquequeue" selected>', content)
|
|
2596
2596
|
self.assertInHTML(
|
|
2597
|
-
'<input type="text" name="var" value="456" class="form-control" required placeholder="None" id="id_var">',
|
|
2597
|
+
'<input type="text" name="var" value="456" class="form-control form-control" required placeholder="None" id="id_var">',
|
|
2598
2598
|
content,
|
|
2599
2599
|
)
|
|
2600
2600
|
self.assertInHTML('<input type="hidden" name="_profile" value="True" id="id__profile">', content)
|
|
@@ -2994,27 +2994,6 @@ class JobButtonRenderingTestCase(TestCase):
|
|
|
2994
2994
|
)
|
|
2995
2995
|
|
|
2996
2996
|
|
|
2997
|
-
class JobCustomTemplateTestCase(TestCase):
|
|
2998
|
-
@classmethod
|
|
2999
|
-
def setUpTestData(cls):
|
|
3000
|
-
# Job model objects are automatically created during database migrations
|
|
3001
|
-
|
|
3002
|
-
# But we do need to make sure the ones we're testing are flagged appropriately
|
|
3003
|
-
cls.example_job = Job.objects.get(job_class_name="ExampleCustomFormJob")
|
|
3004
|
-
cls.example_job.enabled = True
|
|
3005
|
-
cls.example_job.save()
|
|
3006
|
-
|
|
3007
|
-
cls.run_url = reverse("extras:job_run", kwargs={"pk": cls.example_job.pk})
|
|
3008
|
-
|
|
3009
|
-
def test_rendering_custom_template(self):
|
|
3010
|
-
obj_perm = ObjectPermission(name="Test permission", actions=["view", "run"])
|
|
3011
|
-
obj_perm.save()
|
|
3012
|
-
obj_perm.users.add(self.user)
|
|
3013
|
-
obj_perm.object_types.add(ContentType.objects.get_for_model(Job))
|
|
3014
|
-
with self.assertTemplateUsed("example_app/custom_job_form.html"):
|
|
3015
|
-
self.client.get(self.run_url)
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
2997
|
# TODO: Convert to StandardTestCases.Views
|
|
3019
2998
|
class ObjectChangeTestCase(TestCase):
|
|
3020
2999
|
user_permissions = ("extras.view_objectchange",)
|
|
@@ -3050,7 +3029,8 @@ class ObjectChangeTestCase(TestCase):
|
|
|
3050
3029
|
|
|
3051
3030
|
|
|
3052
3031
|
class ObjectMetadataTestCase(
|
|
3053
|
-
ViewTestCases.
|
|
3032
|
+
ViewTestCases.DeleteObjectViewTestCase,
|
|
3033
|
+
ViewTestCases.BulkDeleteObjectsViewTestCase,
|
|
3054
3034
|
ViewTestCases.GetObjectChangelogViewTestCase,
|
|
3055
3035
|
ViewTestCases.ListObjectsViewTestCase,
|
|
3056
3036
|
):
|
nautobot/extras/utils.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import collections
|
|
2
|
-
import contextlib
|
|
3
2
|
import hashlib
|
|
4
3
|
import hmac
|
|
5
4
|
import logging
|
|
@@ -15,7 +14,6 @@ from django.db import transaction
|
|
|
15
14
|
from django.db.models import Q
|
|
16
15
|
from django.template.loader import get_template, TemplateDoesNotExist
|
|
17
16
|
from django.utils.deconstruct import deconstructible
|
|
18
|
-
import redis.exceptions
|
|
19
17
|
|
|
20
18
|
from nautobot.core.choices import ColorChoices
|
|
21
19
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
@@ -111,17 +109,12 @@ class ChangeLoggedModelsQuery(FeaturedQueryMixin):
|
|
|
111
109
|
def change_logged_models_queryset():
|
|
112
110
|
"""
|
|
113
111
|
Cacheable function for cases where we need this queryset many times, such as when saving multiple objects.
|
|
114
|
-
|
|
115
|
-
Cache is cleared by post_migrate signal (nautobot.extras.signals.post_migrate_clear_content_type_caches).
|
|
116
112
|
"""
|
|
117
|
-
queryset = None
|
|
118
113
|
cache_key = "nautobot.extras.utils.change_logged_models_queryset"
|
|
119
|
-
|
|
120
|
-
queryset = cache.get(cache_key)
|
|
114
|
+
queryset = cache.get(cache_key)
|
|
121
115
|
if queryset is None:
|
|
122
116
|
queryset = ChangeLoggedModelsQuery().as_queryset()
|
|
123
|
-
|
|
124
|
-
cache.set(cache_key, queryset)
|
|
117
|
+
cache.set(cache_key, queryset)
|
|
125
118
|
return queryset
|
|
126
119
|
|
|
127
120
|
|
|
@@ -167,7 +160,7 @@ class FeatureQuery:
|
|
|
167
160
|
"""
|
|
168
161
|
Given an extras feature, return a iterable of app_label: [models] for content type lookup.
|
|
169
162
|
|
|
170
|
-
|
|
163
|
+
Mis-named, as it returns an iterable of (key, value) (i.e. dict.items()) rather than an actual dict.
|
|
171
164
|
|
|
172
165
|
Raises a KeyError if the given feature doesn't exist.
|
|
173
166
|
"""
|
|
@@ -180,34 +173,12 @@ class FeatureQuery:
|
|
|
180
173
|
|
|
181
174
|
>>> FeatureQuery('statuses').get_choices()
|
|
182
175
|
[('dcim.device', 13), ('dcim.rack', 34)]
|
|
183
|
-
|
|
184
|
-
Cache is cleared by post_migrate signal (nautobot.extras.signals.post_migrate_clear_content_type_caches).
|
|
185
176
|
"""
|
|
186
|
-
|
|
187
|
-
cache_key = f"nautobot.extras.utils.FeatureQuery.choices.{self.feature}"
|
|
188
|
-
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
189
|
-
choices = cache.get(cache_key)
|
|
190
|
-
if choices is None:
|
|
191
|
-
choices = [(f"{ct.app_label}.{ct.model}", ct.pk) for ct in ContentType.objects.filter(self.get_query())]
|
|
192
|
-
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
193
|
-
cache.set(cache_key, choices)
|
|
194
|
-
return choices
|
|
177
|
+
return [(f"{ct.app_label}.{ct.model}", ct.pk) for ct in ContentType.objects.filter(self.get_query())]
|
|
195
178
|
|
|
196
179
|
def list_subclasses(self):
|
|
197
|
-
"""
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
Cache is cleared by post_migrate signal (nautobot.extras.signals.post_migrate_clear_content_type_caches).
|
|
201
|
-
"""
|
|
202
|
-
subclasses = None
|
|
203
|
-
cache_key = f"nautobot.extras.utils.FeatureQuery.subclasses.{self.feature}"
|
|
204
|
-
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
205
|
-
subclasses = cache.get(cache_key)
|
|
206
|
-
if subclasses is None:
|
|
207
|
-
subclasses = [ct.model_class() for ct in ContentType.objects.filter(self.get_query())]
|
|
208
|
-
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
209
|
-
cache.set(cache_key, subclasses)
|
|
210
|
-
return subclasses
|
|
180
|
+
"""Return a list of model classes that declare this feature."""
|
|
181
|
+
return [ct.model_class() for ct in ContentType.objects.filter(self.get_query())]
|
|
211
182
|
|
|
212
183
|
|
|
213
184
|
@deconstructible
|
nautobot/extras/views.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from datetime import timedelta
|
|
1
2
|
import logging
|
|
2
3
|
from urllib.parse import parse_qs
|
|
3
4
|
|
|
@@ -1387,25 +1388,55 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
|
|
|
1387
1388
|
schedule_type = schedule_form.cleaned_data["_schedule_type"]
|
|
1388
1389
|
|
|
1389
1390
|
if (not dryrun and job_model.approval_required) or schedule_type in JobExecutionType.SCHEDULE_CHOICES:
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1391
|
+
crontab = ""
|
|
1392
|
+
|
|
1393
|
+
if schedule_type == JobExecutionType.TYPE_IMMEDIATELY:
|
|
1394
|
+
# The job must be approved.
|
|
1395
|
+
# If the schedule_type is immediate, we still create the task, but mark it for approval
|
|
1396
|
+
# as a once in the future task with the due date set to the current time. This means
|
|
1397
|
+
# when approval is granted, the task is immediately due for execution.
|
|
1398
|
+
schedule_type = JobExecutionType.TYPE_FUTURE
|
|
1399
|
+
schedule_datetime = timezone.now()
|
|
1400
|
+
schedule_name = f"{job_model} - {schedule_datetime}"
|
|
1401
|
+
|
|
1402
|
+
else:
|
|
1403
|
+
schedule_name = schedule_form.cleaned_data["_schedule_name"]
|
|
1404
|
+
|
|
1405
|
+
if schedule_type == JobExecutionType.TYPE_CUSTOM:
|
|
1406
|
+
crontab = schedule_form.cleaned_data["_recurrence_custom_time"]
|
|
1407
|
+
# doing .get("key", "default") returns None instead of "default" here for some reason
|
|
1408
|
+
schedule_datetime = schedule_form.cleaned_data.get("_schedule_start_time")
|
|
1409
|
+
if schedule_datetime is None:
|
|
1410
|
+
# "_schedule_start_time" is checked against ScheduledJob.earliest_possible_time()
|
|
1411
|
+
# which returns timezone.now() + timedelta(seconds=15)
|
|
1412
|
+
schedule_datetime = timezone.now() + timedelta(seconds=20)
|
|
1413
|
+
else:
|
|
1414
|
+
schedule_datetime = schedule_form.cleaned_data["_schedule_start_time"]
|
|
1415
|
+
|
|
1416
|
+
celery_kwargs = {"nautobot_job_profile": profile, "queue": task_queue}
|
|
1417
|
+
scheduled_job = ScheduledJob(
|
|
1418
|
+
name=schedule_name,
|
|
1419
|
+
task=job_model.class_path,
|
|
1420
|
+
job_model=job_model,
|
|
1421
|
+
start_time=schedule_datetime,
|
|
1422
|
+
description=f"Nautobot job {schedule_name} scheduled by {request.user} for {schedule_datetime}",
|
|
1423
|
+
kwargs=job_class.serialize_data(job_form.cleaned_data),
|
|
1424
|
+
celery_kwargs=celery_kwargs,
|
|
1395
1425
|
interval=schedule_type,
|
|
1396
|
-
|
|
1426
|
+
one_off=schedule_type == JobExecutionType.TYPE_FUTURE,
|
|
1427
|
+
queue=task_queue,
|
|
1428
|
+
user=request.user,
|
|
1397
1429
|
approval_required=job_model.approval_required,
|
|
1398
|
-
|
|
1399
|
-
profile=profile,
|
|
1400
|
-
**job_class.serialize_data(job_form.cleaned_data),
|
|
1430
|
+
crontab=crontab,
|
|
1401
1431
|
)
|
|
1432
|
+
scheduled_job.validated_save()
|
|
1402
1433
|
|
|
1403
1434
|
if job_model.approval_required:
|
|
1404
|
-
messages.success(request, f"Job {
|
|
1405
|
-
return redirect(return_url
|
|
1435
|
+
messages.success(request, f"Job {schedule_name} successfully submitted for approval")
|
|
1436
|
+
return redirect(return_url if return_url else "extras:scheduledjob_approval_queue_list")
|
|
1406
1437
|
else:
|
|
1407
|
-
messages.success(request, f"Job {
|
|
1408
|
-
return redirect(return_url
|
|
1438
|
+
messages.success(request, f"Job {schedule_name} successfully scheduled")
|
|
1439
|
+
return redirect(return_url if return_url else "extras:scheduledjob_list")
|
|
1409
1440
|
|
|
1410
1441
|
else:
|
|
1411
1442
|
# Enqueue job for immediate execution
|
|
@@ -1691,12 +1722,7 @@ class SavedViewUIViewSet(
|
|
|
1691
1722
|
view_name = new_global_default_view.view
|
|
1692
1723
|
message = ""
|
|
1693
1724
|
if new_global_default_view.is_global_default:
|
|
1694
|
-
message =
|
|
1695
|
-
'<br>The global default saved view for "{}" is set to <a href="{}">{}</a>',
|
|
1696
|
-
view_name,
|
|
1697
|
-
new_global_default_view.get_absolute_url(),
|
|
1698
|
-
new_global_default_view.name,
|
|
1699
|
-
)
|
|
1725
|
+
message += f"<br>The global default saved View for '{view_name}' is set to <a href='{new_global_default_view.get_absolute_url()}'>{new_global_default_view.name}</a>."
|
|
1700
1726
|
return message
|
|
1701
1727
|
|
|
1702
1728
|
def list(self, request, *args, **kwargs):
|
|
@@ -2050,13 +2076,8 @@ class JobLogEntryTableView(generic.GenericView):
|
|
|
2050
2076
|
else:
|
|
2051
2077
|
queryset = instance.job_log_entries.all()
|
|
2052
2078
|
log_table = tables.JobLogEntryTable(data=queryset, user=request.user)
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
"per_page": get_paginate_count(request),
|
|
2056
|
-
}
|
|
2057
|
-
RequestConfig(request, paginate).configure(log_table)
|
|
2058
|
-
table = log_table.as_html(request)
|
|
2059
|
-
return HttpResponse(table)
|
|
2079
|
+
RequestConfig(request).configure(log_table)
|
|
2080
|
+
return HttpResponse(log_table.as_html(request))
|
|
2060
2081
|
|
|
2061
2082
|
|
|
2062
2083
|
#
|
|
@@ -2216,13 +2237,15 @@ class MetadataTypeUIViewSet(NautobotUIViewSet):
|
|
|
2216
2237
|
|
|
2217
2238
|
|
|
2218
2239
|
class ObjectMetadataUIViewSet(
|
|
2240
|
+
ObjectBulkDestroyViewMixin,
|
|
2219
2241
|
ObjectChangeLogViewMixin,
|
|
2242
|
+
ObjectDestroyViewMixin,
|
|
2220
2243
|
ObjectDetailViewMixin,
|
|
2221
2244
|
ObjectListViewMixin,
|
|
2222
2245
|
):
|
|
2223
2246
|
filterset_class = filters.ObjectMetadataFilterSet
|
|
2224
2247
|
filterset_form_class = forms.ObjectMetadataFilterForm
|
|
2225
|
-
queryset = ObjectMetadata.objects.all().order_by("assigned_object_type", "
|
|
2248
|
+
queryset = ObjectMetadata.objects.all().order_by("assigned_object_type", "scoped_fields")
|
|
2226
2249
|
serializer_class = serializers.ObjectMetadataSerializer
|
|
2227
2250
|
table_class = tables.ObjectMetadataTable
|
|
2228
2251
|
action_buttons = ("export",)
|
nautobot/ipam/filters.py
CHANGED
|
@@ -498,7 +498,7 @@ class IPAddressFilterSet(
|
|
|
498
498
|
return queryset.none()
|
|
499
499
|
interface_ids = []
|
|
500
500
|
for device in devices:
|
|
501
|
-
interface_ids.extend(device.
|
|
501
|
+
interface_ids.extend(device.all_interfaces.values_list("id", flat=True))
|
|
502
502
|
return queryset.filter(interfaces__in=interface_ids)
|
|
503
503
|
|
|
504
504
|
def filter_virtual_machine(self, queryset, name, value):
|
nautobot/ipam/forms.py
CHANGED
|
@@ -899,7 +899,7 @@ class ServiceForm(NautobotModelForm):
|
|
|
899
899
|
# Limit IP address choices to those assigned to interfaces of the parent device/VM
|
|
900
900
|
if self.instance.device:
|
|
901
901
|
self.fields["ip_addresses"].queryset = IPAddress.objects.filter(
|
|
902
|
-
interfaces__in=self.instance.device.
|
|
902
|
+
interfaces__in=self.instance.device.all_interfaces.values_list("id", flat=True)
|
|
903
903
|
)
|
|
904
904
|
elif self.instance.virtual_machine:
|
|
905
905
|
self.fields["ip_addresses"].queryset = IPAddress.objects.filter(
|
nautobot/ipam/models.py
CHANGED
|
@@ -49,6 +49,7 @@ logger = logging.getLogger(__name__)
|
|
|
49
49
|
@extras_features(
|
|
50
50
|
"custom_links",
|
|
51
51
|
"custom_validators",
|
|
52
|
+
"dynamic_groups",
|
|
52
53
|
"export_templates",
|
|
53
54
|
"graphql",
|
|
54
55
|
"locations",
|
|
@@ -398,6 +399,7 @@ class RIR(OrganizationalModel):
|
|
|
398
399
|
@extras_features(
|
|
399
400
|
"custom_links",
|
|
400
401
|
"custom_validators",
|
|
402
|
+
"dynamic_groups",
|
|
401
403
|
"export_templates",
|
|
402
404
|
"graphql",
|
|
403
405
|
"locations",
|
|
@@ -499,6 +501,11 @@ class Prefix(PrimaryModel):
|
|
|
499
501
|
"type",
|
|
500
502
|
"vlan",
|
|
501
503
|
]
|
|
504
|
+
"""
|
|
505
|
+
dynamic_group_filter_fields = {
|
|
506
|
+
"vrf": "vrf_id", # Duplicate filter fields that will be collapsed in 2.0
|
|
507
|
+
}
|
|
508
|
+
"""
|
|
502
509
|
|
|
503
510
|
class Meta:
|
|
504
511
|
ordering = (
|
|
@@ -979,6 +986,7 @@ class PrefixLocationAssignment(BaseModel):
|
|
|
979
986
|
@extras_features(
|
|
980
987
|
"custom_links",
|
|
981
988
|
"custom_validators",
|
|
989
|
+
"dynamic_groups",
|
|
982
990
|
"export_templates",
|
|
983
991
|
"graphql",
|
|
984
992
|
"statuses",
|
|
@@ -1012,7 +1020,7 @@ class IPAddress(PrimaryModel):
|
|
|
1012
1020
|
parent = models.ForeignKey(
|
|
1013
1021
|
"ipam.Prefix",
|
|
1014
1022
|
blank=True,
|
|
1015
|
-
null=True,
|
|
1023
|
+
null=True,
|
|
1016
1024
|
related_name="ip_addresses", # `IPAddress` to use `related_name="ip_addresses"`
|
|
1017
1025
|
on_delete=models.PROTECT,
|
|
1018
1026
|
help_text="The parent Prefix of this IPAddress.",
|
|
@@ -1109,7 +1117,7 @@ class IPAddress(PrimaryModel):
|
|
|
1109
1117
|
raise ValidationError({"namespace": "No suitable parent Prefix exists in this Namespace"}) from e
|
|
1110
1118
|
|
|
1111
1119
|
def clean(self):
|
|
1112
|
-
|
|
1120
|
+
super().clean()
|
|
1113
1121
|
|
|
1114
1122
|
# Validate that host is not being modified
|
|
1115
1123
|
if self.present_in_database:
|
|
@@ -1123,8 +1131,8 @@ class IPAddress(PrimaryModel):
|
|
|
1123
1131
|
|
|
1124
1132
|
closest_parent = self._get_closest_parent()
|
|
1125
1133
|
# Validate `parent` can be used as the parent for this ipaddress
|
|
1126
|
-
if
|
|
1127
|
-
if self.parent
|
|
1134
|
+
if self.parent and closest_parent:
|
|
1135
|
+
if self.parent != closest_parent:
|
|
1128
1136
|
raise ValidationError(
|
|
1129
1137
|
{
|
|
1130
1138
|
"parent": (
|
|
@@ -1136,20 +1144,23 @@ class IPAddress(PrimaryModel):
|
|
|
1136
1144
|
self.parent = closest_parent
|
|
1137
1145
|
self._namespace = None
|
|
1138
1146
|
|
|
1147
|
+
def save(self, *args, **kwargs):
|
|
1139
1148
|
# 3.0 TODO: uncomment the below to enforce this constraint
|
|
1140
1149
|
# if self.parent.type != choices.PrefixTypeChoices.TYPE_NETWORK:
|
|
1141
1150
|
# err_msg = f"IP addresses cannot be created in {self.parent.type} prefixes. You must create a network prefix first."
|
|
1142
1151
|
# raise ValidationError({"address": err_msg})
|
|
1143
1152
|
|
|
1153
|
+
self.address = self.address # not a no-op - forces re-calling of self._deconstruct_address()
|
|
1154
|
+
|
|
1144
1155
|
# Force dns_name to lowercase
|
|
1145
1156
|
if not self.dns_name.islower:
|
|
1146
1157
|
self.dns_name = self.dns_name.lower()
|
|
1147
1158
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1159
|
+
# Host and mask_length are required to get closest parent
|
|
1160
|
+
closest_parent = self._get_closest_parent()
|
|
1161
|
+
if closest_parent is not None:
|
|
1162
|
+
self.parent = closest_parent
|
|
1163
|
+
self._namespace = None
|
|
1153
1164
|
super().save(*args, **kwargs)
|
|
1154
1165
|
|
|
1155
1166
|
@property
|
nautobot/ipam/tables.py
CHANGED
|
@@ -355,9 +355,6 @@ class PrefixTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
355
355
|
location_count = LinkedCountColumn(
|
|
356
356
|
viewname="dcim:location_list", url_params={"prefixes": "pk"}, verbose_name="Locations"
|
|
357
357
|
)
|
|
358
|
-
cloud_networks_count = LinkedCountColumn(
|
|
359
|
-
viewname="cloud:cloudnetwork_list", url_params={"prefixes": "pk"}, verbose_name="Cloud Networks"
|
|
360
|
-
)
|
|
361
358
|
|
|
362
359
|
class Meta(BaseTable.Meta):
|
|
363
360
|
model = Prefix
|
|
@@ -371,7 +368,6 @@ class PrefixTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
371
368
|
"namespace",
|
|
372
369
|
"tenant",
|
|
373
370
|
"location_count",
|
|
374
|
-
"cloud_networks_count",
|
|
375
371
|
"vlan",
|
|
376
372
|
"role",
|
|
377
373
|
"rir",
|
|
@@ -1003,9 +1003,8 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
|
|
|
1003
1003
|
def test_duplicate_global_unique(self):
|
|
1004
1004
|
"""Test that duplicate IPs in the same Namespace raises an error."""
|
|
1005
1005
|
IPAddress.objects.create(address="192.0.2.1/24", status=self.status, namespace=self.namespace)
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
duplicate_ip.full_clean()
|
|
1006
|
+
with self.assertRaises(IntegrityError):
|
|
1007
|
+
IPAddress.objects.create(address="192.0.2.1/24", status=self.status, namespace=self.namespace)
|
|
1009
1008
|
|
|
1010
1009
|
def test_multiple_nat_outside_list(self):
|
|
1011
1010
|
"""
|
nautobot/ipam/views.py
CHANGED
|
@@ -15,7 +15,7 @@ from django.views.generic import View
|
|
|
15
15
|
from django_tables2 import RequestConfig
|
|
16
16
|
import netaddr
|
|
17
17
|
|
|
18
|
-
from nautobot.cloud.tables import
|
|
18
|
+
from nautobot.cloud.tables import CloudNetworkPrefixAssignmentTable
|
|
19
19
|
from nautobot.core.models.querysets import count_related
|
|
20
20
|
from nautobot.core.utils.config import get_settings_or_config
|
|
21
21
|
from nautobot.core.utils.permissions import get_permission_for_model
|
|
@@ -456,9 +456,8 @@ class PrefixView(generic.ObjectView):
|
|
|
456
456
|
vrfs = instance.vrf_assignments.restrict(request.user, "view")
|
|
457
457
|
vrf_table = tables.VRFPrefixAssignmentTable(vrfs, orderable=False)
|
|
458
458
|
|
|
459
|
-
cloud_networks = instance.
|
|
460
|
-
cloud_network_table =
|
|
461
|
-
cloud_network_table.exclude = ("actions", "assigned_prefix_count", "circuit_count", "cloud_service_count")
|
|
459
|
+
cloud_networks = instance.cloud_network_assignments.restrict(request.user, "view")
|
|
460
|
+
cloud_network_table = CloudNetworkPrefixAssignmentTable(cloud_networks, orderable=False)
|
|
462
461
|
|
|
463
462
|
return {
|
|
464
463
|
"vrf_table": vrf_table,
|
|
@@ -935,8 +934,14 @@ class IPAddressAssignView(view_mixins.GetReturnURLMixin, generic.ObjectView):
|
|
|
935
934
|
ip_addresses = IPAddress.objects.restrict(request.user, "view").filter(pk__in=pks)
|
|
936
935
|
interface.ip_addresses.add(*ip_addresses)
|
|
937
936
|
return redirect(self.get_return_url(request))
|
|
938
|
-
|
|
939
|
-
return
|
|
937
|
+
|
|
938
|
+
return render(
|
|
939
|
+
request,
|
|
940
|
+
"ipam/ipaddress_assign.html",
|
|
941
|
+
{
|
|
942
|
+
"return_url": self.get_return_url(request),
|
|
943
|
+
},
|
|
944
|
+
)
|
|
940
945
|
|
|
941
946
|
|
|
942
947
|
class IPAddressMergeView(view_mixins.GetReturnURLMixin, view_mixins.ObjectPermissionRequiredMixin, View):
|