nautobot 2.3.1__py3-none-any.whl → 2.3.3__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.
- nautobot/core/celery/schedulers.py +18 -0
- nautobot/core/settings.yaml +3 -3
- nautobot/core/tables.py +1 -1
- nautobot/core/templates/home.html +4 -3
- nautobot/core/templatetags/buttons.py +1 -1
- nautobot/core/tests/runner.py +27 -9
- nautobot/core/tests/test_utils.py +13 -0
- nautobot/core/utils/lookup.py +7 -1
- nautobot/core/views/utils.py +3 -3
- nautobot/dcim/factory.py +3 -3
- nautobot/dcim/tables/devices.py +7 -7
- nautobot/dcim/templates/dcim/device.html +12 -0
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +12 -0
- nautobot/dcim/utils.py +9 -6
- nautobot/extras/api/serializers.py +2 -0
- nautobot/extras/context_managers.py +11 -4
- nautobot/extras/filters/__init__.py +14 -2
- nautobot/extras/forms/forms.py +6 -0
- nautobot/extras/forms/mixins.py +2 -2
- nautobot/extras/jobs.py +0 -1
- nautobot/extras/management/__init__.py +3 -0
- nautobot/extras/migrations/0115_scheduledjob_time_zone.py +23 -0
- nautobot/extras/models/groups.py +4 -1
- nautobot/extras/models/jobs.py +24 -11
- nautobot/extras/tables.py +34 -4
- nautobot/extras/templates/extras/scheduledjob.html +13 -2
- nautobot/extras/tests/test_api.py +17 -18
- nautobot/extras/tests/test_context_managers.py +33 -14
- nautobot/extras/tests/test_dynamicgroups.py +11 -0
- nautobot/extras/tests/test_filters.py +57 -1
- nautobot/extras/tests/test_models.py +304 -1
- nautobot/extras/tests/test_views.py +4 -2
- nautobot/extras/views.py +7 -0
- nautobot/ipam/api/views.py +9 -2
- nautobot/ipam/choices.py +17 -0
- nautobot/ipam/factory.py +6 -0
- nautobot/ipam/filters.py +1 -1
- nautobot/ipam/forms.py +5 -3
- nautobot/ipam/migrations/0048_vrf_status.py +23 -0
- nautobot/ipam/migrations/0049_vrf_data_migration.py +25 -0
- nautobot/ipam/models.py +6 -0
- nautobot/ipam/tables.py +3 -2
- nautobot/ipam/templates/ipam/vrf.html +4 -0
- nautobot/ipam/templates/ipam/vrf_edit.html +1 -0
- nautobot/ipam/tests/test_api.py +44 -3
- nautobot/ipam/tests/test_views.py +3 -0
- nautobot/project-static/css/base.css +6 -0
- nautobot/project-static/docs/404.html +23 -23
- nautobot/project-static/docs/apps/index.html +25 -25
- nautobot/project-static/docs/apps/nautobot-apps.html +24 -24
- nautobot/project-static/docs/assets/javascripts/bundle.56dfad97.min.js +16 -0
- nautobot/project-static/docs/assets/javascripts/bundle.56dfad97.min.js.map +7 -0
- nautobot/project-static/docs/assets/javascripts/workers/{search.b8dbb3d2.min.js → search.07f07601.min.js} +1 -1
- nautobot/project-static/docs/assets/javascripts/workers/{search.b8dbb3d2.min.js.map → search.07f07601.min.js.map} +1 -1
- nautobot/project-static/docs/assets/stylesheets/main.35f28582.min.css +1 -0
- nautobot/project-static/docs/assets/stylesheets/main.35f28582.min.css.map +1 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +26 -26
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +26 -26
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +58 -58
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +32 -31
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +31 -31
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +25 -25
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +25 -25
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +30 -30
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +34 -34
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +41 -41
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +48 -48
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +92 -92
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +41 -41
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +85 -85
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +84 -84
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +26 -26
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +28 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +40 -40
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +78 -78
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +77 -77
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +26 -26
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +80 -80
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +67 -67
- nautobot/project-static/docs/development/apps/api/configuration-view.html +25 -25
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +25 -25
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +25 -25
- nautobot/project-static/docs/development/apps/api/models/global-search.html +25 -25
- nautobot/project-static/docs/development/apps/api/models/graphql.html +25 -25
- nautobot/project-static/docs/development/apps/api/models/index.html +25 -25
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +25 -25
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +25 -25
- nautobot/project-static/docs/development/apps/api/prometheus.html +25 -25
- nautobot/project-static/docs/development/apps/api/setup.html +25 -25
- nautobot/project-static/docs/development/apps/api/testing.html +25 -25
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +25 -25
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +25 -25
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +25 -25
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +25 -25
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/base-template.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/index.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/notes.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +25 -25
- nautobot/project-static/docs/development/apps/api/views/urls.html +25 -25
- nautobot/project-static/docs/development/apps/index.html +25 -25
- nautobot/project-static/docs/development/apps/migration/code-updates.html +25 -25
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +25 -25
- nautobot/project-static/docs/development/apps/migration/from-v1.html +25 -25
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +25 -25
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +25 -25
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +25 -25
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +25 -25
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +25 -25
- nautobot/project-static/docs/development/core/application-registry.html +25 -25
- nautobot/project-static/docs/development/core/best-practices.html +25 -25
- nautobot/project-static/docs/development/core/bootstrap-ui.html +25 -25
- nautobot/project-static/docs/development/core/caching.html +25 -25
- nautobot/project-static/docs/development/core/controllers.html +25 -25
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +25 -25
- nautobot/project-static/docs/development/core/generic-views.html +25 -25
- nautobot/project-static/docs/development/core/getting-started.html +25 -25
- nautobot/project-static/docs/development/core/homepage.html +25 -25
- nautobot/project-static/docs/development/core/index.html +25 -25
- nautobot/project-static/docs/development/core/model-checklist.html +25 -25
- nautobot/project-static/docs/development/core/model-features.html +25 -25
- nautobot/project-static/docs/development/core/natural-keys.html +25 -25
- nautobot/project-static/docs/development/core/navigation-menu.html +25 -25
- nautobot/project-static/docs/development/core/release-checklist.html +25 -25
- nautobot/project-static/docs/development/core/role-internals.html +25 -25
- nautobot/project-static/docs/development/core/settings.html +25 -25
- nautobot/project-static/docs/development/core/style-guide.html +25 -25
- nautobot/project-static/docs/development/core/templates.html +25 -25
- nautobot/project-static/docs/development/core/testing.html +25 -25
- nautobot/project-static/docs/development/core/user-preferences.html +25 -25
- nautobot/project-static/docs/development/index.html +25 -25
- nautobot/project-static/docs/development/jobs/index.html +25 -25
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +25 -25
- nautobot/project-static/docs/index.html +30 -30
- nautobot/project-static/docs/overview/application_stack.html +31 -31
- nautobot/project-static/docs/overview/design_philosophy.html +25 -25
- nautobot/project-static/docs/release-notes/index.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.0.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.1.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.2.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.3.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.4.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.5.html +25 -25
- nautobot/project-static/docs/release-notes/version-1.6.html +25 -25
- nautobot/project-static/docs/release-notes/version-2.0.html +25 -25
- nautobot/project-static/docs/release-notes/version-2.1.html +25 -25
- nautobot/project-static/docs/release-notes/version-2.2.html +25 -25
- nautobot/project-static/docs/release-notes/version-2.3.html +318 -61
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +271 -542
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +25 -25
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +25 -25
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +25 -25
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +25 -25
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +28 -28
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +25 -25
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +25 -25
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/index.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation/services.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +25 -25
- nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +25 -25
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +25 -25
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +25 -25
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +25 -25
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +25 -25
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +25 -25
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +25 -25
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +25 -25
- nautobot/project-static/docs/user-guide/index.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +25 -25
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +25 -25
- nautobot/project-static/js/homepage_layout.js +3 -0
- {nautobot-2.3.1.dist-info → nautobot-2.3.3.dist-info}/METADATA +4 -4
- {nautobot-2.3.1.dist-info → nautobot-2.3.3.dist-info}/RECORD +335 -332
- nautobot/project-static/docs/assets/javascripts/bundle.fe8b6f2b.min.js +0 -29
- nautobot/project-static/docs/assets/javascripts/bundle.fe8b6f2b.min.js.map +0 -7
- 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.1.dist-info → nautobot-2.3.3.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.1.dist-info → nautobot-2.3.3.dist-info}/NOTICE +0 -0
- {nautobot-2.3.1.dist-info → nautobot-2.3.3.dist-info}/WHEEL +0 -0
- {nautobot-2.3.1.dist-info → nautobot-2.3.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from collections.abc import Mapping
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
import logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
@@ -20,7 +21,10 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
20
21
|
|
|
21
22
|
def __init__(self, model, app=None):
|
|
22
23
|
"""Initialize the model entry."""
|
|
24
|
+
# copy-paste from django_celery_beat.schedulers
|
|
23
25
|
self.app = app or current_app._get_current_object()
|
|
26
|
+
|
|
27
|
+
# Nautobot-specific logic
|
|
24
28
|
self.name = f"{model.name}_{model.pk}"
|
|
25
29
|
self.task = "nautobot.extras.jobs.run_job"
|
|
26
30
|
try:
|
|
@@ -33,6 +37,8 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
33
37
|
except (TypeError, ValueError) as exc:
|
|
34
38
|
logger.exception("Removing schedule %s for argument deserialization error: %s", self.name, exc)
|
|
35
39
|
self._disable(model)
|
|
40
|
+
|
|
41
|
+
# copy-paste from django_celery_beat.schedulers
|
|
36
42
|
try:
|
|
37
43
|
self.schedule = model.schedule
|
|
38
44
|
except model.DoesNotExist:
|
|
@@ -42,6 +48,7 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
42
48
|
)
|
|
43
49
|
self._disable(model)
|
|
44
50
|
|
|
51
|
+
# Nautobot-specific logic
|
|
45
52
|
self.options = {"nautobot_job_scheduled_job_id": model.id, "headers": {}}
|
|
46
53
|
|
|
47
54
|
if model.user:
|
|
@@ -65,14 +72,25 @@ class NautobotScheduleEntry(ModelEntry):
|
|
|
65
72
|
if isinstance(model.celery_kwargs, Mapping):
|
|
66
73
|
self.options.update(model.celery_kwargs)
|
|
67
74
|
|
|
75
|
+
# copy-paste from django_celery_beat.schedulers
|
|
68
76
|
self.total_run_count = model.total_run_count
|
|
69
77
|
self.model = model
|
|
70
78
|
|
|
71
79
|
if not model.last_run_at:
|
|
72
80
|
model.last_run_at = self._default_now()
|
|
81
|
+
# if last_run_at is not set and
|
|
82
|
+
# model.start_time last_run_at should be in way past.
|
|
83
|
+
# This will trigger the job to run at start_time
|
|
84
|
+
# and avoid the heap block.
|
|
85
|
+
if model.start_time:
|
|
86
|
+
model.last_run_at = model.last_run_at - datetime.timedelta(days=365 * 30)
|
|
73
87
|
|
|
74
88
|
self.last_run_at = model.last_run_at
|
|
75
89
|
|
|
90
|
+
def _default_now(self):
|
|
91
|
+
"""Instead of using self.app.timezone, use the timezone specific to this schedule entry."""
|
|
92
|
+
return datetime.now(self.model.time_zone)
|
|
93
|
+
|
|
76
94
|
|
|
77
95
|
class NautobotDatabaseScheduler(DatabaseScheduler):
|
|
78
96
|
"""
|
nautobot/core/settings.yaml
CHANGED
|
@@ -1830,9 +1830,9 @@ properties:
|
|
|
1830
1830
|
The time zone Nautobot will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. Please see the [list of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
|
|
1831
1831
|
details: |-
|
|
1832
1832
|
!!! warning
|
|
1833
|
-
Scheduled jobs will
|
|
1834
|
-
default UTC,
|
|
1835
|
-
jobs may run in the wrong time zone.
|
|
1833
|
+
Scheduled jobs will default to running in the time zone configured in this setting.
|
|
1834
|
+
If you change this setting from the default UTC, it must be set consistently on the Celery Beat server
|
|
1835
|
+
and all Nautobot web servers, or else your scheduled jobs may run in the wrong time zone.
|
|
1836
1836
|
environment_variable: "NAUTOBOT_TIME_ZONE"
|
|
1837
1837
|
see_also:
|
|
1838
1838
|
"Time Zones documentation": "./time-zones.md"
|
nautobot/core/tables.py
CHANGED
|
@@ -49,7 +49,7 @@ class BaseTable(django_tables2.Table):
|
|
|
49
49
|
# Add custom field columns
|
|
50
50
|
model = self._meta.model
|
|
51
51
|
|
|
52
|
-
if model
|
|
52
|
+
if getattr(model, "is_dynamic_group_associable_model", False):
|
|
53
53
|
self.base_columns["dynamic_group_count"] = LinkedCountColumn(
|
|
54
54
|
viewname="extras:dynamicgroup_list",
|
|
55
55
|
url_params={"member_id": "pk"},
|
|
@@ -39,10 +39,11 @@
|
|
|
39
39
|
{% for panel_name, panel_details in registry.homepage_layout.panels.items %}
|
|
40
40
|
{% if request.user|has_one_or_more_perms:panel_details.permissions %}
|
|
41
41
|
<div class="panel panel-default" id="{{ panel_name|slugify }}" style="break-inside: avoid" data-panel-weight="{{ panel_details.weight }}">
|
|
42
|
-
<div class="panel-heading">
|
|
43
|
-
<strong>{{ panel_name }}</strong><span id="toggle-homepanel-{{ panel_name|slugify }}" class="glyphicon glyphicon-chevron-down collapse-icon" type="button" data-toggle="collapse" data-target="#homepanel-{{ panel_name|slugify }}" aria-expanded="false" aria-controls="homepanel-{{ panel_name|slugify }}"></span>
|
|
44
|
-
</div>
|
|
45
42
|
{% with cookie_key='homepanel-'|add:panel_name|slugify %}
|
|
43
|
+
<div class="panel-heading">
|
|
44
|
+
<strong>{{ panel_name }}</strong>
|
|
45
|
+
<span id="collapse-icon-{{ panel_name|slugify }}" class="glyphicon glyphicon-chevron-down collapse-icon{% if request.COOKIES|default:''|get_item:cookie_key|default:'False' == 'False' %} rotated180{% endif %}" type="button" data-toggle="collapse" data-target="#homepanel-{{ panel_name|slugify }}" aria-expanded="false" aria-controls="homepanel-{{ panel_name|slugify }}"></span>
|
|
46
|
+
</div>
|
|
46
47
|
<div class="list-group collapse{% if request.COOKIES|default:''|get_item:cookie_key|default:'False' == 'False' %} in{% endif %} collapsible-div" id="homepanel-{{ panel_name|slugify }}" >
|
|
47
48
|
{% endwith %}
|
|
48
49
|
{% if panel_details.rendered_html %}
|
|
@@ -165,7 +165,7 @@ def consolidate_bulk_action_buttons(context):
|
|
|
165
165
|
|
|
166
166
|
render_edit_button = bool(context["bulk_edit_url"] and context["permissions"]["change"])
|
|
167
167
|
render_static_group_assign_button = bool(
|
|
168
|
-
context["model"]
|
|
168
|
+
getattr(context["model"], "is_dynamic_group_associable_model", False)
|
|
169
169
|
and context["user"].has_perms(["extras.add_staticgroupassociation"])
|
|
170
170
|
)
|
|
171
171
|
render_delete_button = bool(context["bulk_delete_url"] and context["permissions"]["delete"])
|
nautobot/core/tests/runner.py
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import hashlib
|
|
3
3
|
|
|
4
|
+
try:
|
|
5
|
+
from coverage import Coverage
|
|
6
|
+
|
|
7
|
+
has_coverage = True
|
|
8
|
+
except ImportError:
|
|
9
|
+
has_coverage = False
|
|
10
|
+
|
|
4
11
|
from django.conf import settings
|
|
5
12
|
from django.core.management import call_command
|
|
6
13
|
from django.db import connections
|
|
@@ -49,6 +56,15 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
49
56
|
|
|
50
57
|
exclude_tags = ["integration"]
|
|
51
58
|
|
|
59
|
+
@classmethod
|
|
60
|
+
def add_arguments(cls, parser):
|
|
61
|
+
super().add_arguments(parser)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--cache-test-fixtures",
|
|
64
|
+
action="store_true",
|
|
65
|
+
help="Save test database to a json fixture file to re-use on subsequent tests.",
|
|
66
|
+
)
|
|
67
|
+
|
|
52
68
|
def __init__(self, cache_test_fixtures=False, **kwargs):
|
|
53
69
|
self.cache_test_fixtures = cache_test_fixtures
|
|
54
70
|
|
|
@@ -64,15 +80,6 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
64
80
|
|
|
65
81
|
super().__init__(**kwargs)
|
|
66
82
|
|
|
67
|
-
@classmethod
|
|
68
|
-
def add_arguments(cls, parser):
|
|
69
|
-
super().add_arguments(parser)
|
|
70
|
-
parser.add_argument(
|
|
71
|
-
"--cache-test-fixtures",
|
|
72
|
-
action="store_true",
|
|
73
|
-
help="Save test database to a json fixture file to re-use on subsequent tests.",
|
|
74
|
-
)
|
|
75
|
-
|
|
76
83
|
def setup_test_environment(self, **kwargs):
|
|
77
84
|
super().setup_test_environment(**kwargs)
|
|
78
85
|
# Remove 'testserver' that Django "helpfully" adds automatically to ALLOWED_HOSTS, masking issues like #3065
|
|
@@ -91,6 +98,13 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
91
98
|
|
|
92
99
|
old_names = []
|
|
93
100
|
|
|
101
|
+
# Nautobot specific - disable coverage measurement to improve performance of (slow) database setup
|
|
102
|
+
cov = None
|
|
103
|
+
if has_coverage:
|
|
104
|
+
cov = Coverage.current()
|
|
105
|
+
if cov is not None:
|
|
106
|
+
cov.stop()
|
|
107
|
+
|
|
94
108
|
for db_name, aliases in test_databases.values():
|
|
95
109
|
first_alias = None
|
|
96
110
|
for alias in aliases:
|
|
@@ -150,6 +164,10 @@ class NautobotTestRunner(DiscoverRunner):
|
|
|
150
164
|
for alias in connections:
|
|
151
165
|
connections[alias].force_debug_cursor = True
|
|
152
166
|
|
|
167
|
+
# Nautobot specific - resume test coverage measurement
|
|
168
|
+
if cov is not None:
|
|
169
|
+
cov.start()
|
|
170
|
+
|
|
153
171
|
return old_names
|
|
154
172
|
|
|
155
173
|
def teardown_databases(self, old_config, **kwargs):
|
|
@@ -234,6 +234,19 @@ class GetFooForModelTest(TestCase):
|
|
|
234
234
|
self.assertEqual(lookup.get_model_from_name("dcim.device"), dcim_models.Device)
|
|
235
235
|
self.assertEqual(lookup.get_model_from_name("dcim.location"), dcim_models.Location)
|
|
236
236
|
|
|
237
|
+
def test_get_model_for_view_name(self):
|
|
238
|
+
"""
|
|
239
|
+
Test the util function `get_model_for_view_name` returns the appropriate Model, if the colon separated view name provided.
|
|
240
|
+
"""
|
|
241
|
+
with self.subTest("Test core view."):
|
|
242
|
+
self.assertEqual(lookup.get_model_for_view_name("dcim:device_list"), dcim_models.Device)
|
|
243
|
+
with self.subTest("Test app view."):
|
|
244
|
+
self.assertEqual(lookup.get_model_for_view_name("plugins:example_app:examplemodel_list"), ExampleModel)
|
|
245
|
+
with self.subTest("Test unexpected view."):
|
|
246
|
+
with self.assertRaises(ValueError) as err:
|
|
247
|
+
lookup.get_model_for_view_name("unknown:plugins:example_app:examplemodel_list")
|
|
248
|
+
self.assertEqual(str(err.exception), "Unexpected View Name: unknown:plugins:example_app:examplemodel_list")
|
|
249
|
+
|
|
237
250
|
|
|
238
251
|
class IsTaggableTest(TestCase):
|
|
239
252
|
def test_is_taggable_true(self):
|
nautobot/core/utils/lookup.py
CHANGED
|
@@ -213,7 +213,13 @@ def get_model_for_view_name(view_name):
|
|
|
213
213
|
Return the model class associated with the given view_name e.g. "circuits:circuit_detail", "dcim:device_list" and etc.
|
|
214
214
|
If the app_label or model_name contained by the given view_name is invalid, this will return `None`.
|
|
215
215
|
"""
|
|
216
|
-
|
|
216
|
+
split_view_name = view_name.split(":")
|
|
217
|
+
if len(split_view_name) == 2:
|
|
218
|
+
app_label, model_name = split_view_name # dcim, device_list
|
|
219
|
+
elif len(split_view_name) == 3:
|
|
220
|
+
_, app_label, model_name = split_view_name # plugins, app_name, model_list
|
|
221
|
+
else:
|
|
222
|
+
raise ValueError(f"Unexpected View Name: {view_name}")
|
|
217
223
|
model_name = model_name.split("_")[0] # device
|
|
218
224
|
|
|
219
225
|
try:
|
nautobot/core/views/utils.py
CHANGED
|
@@ -337,7 +337,7 @@ def common_detail_view_context(request, instance):
|
|
|
337
337
|
context["created_by"] = created_by
|
|
338
338
|
context["last_updated_by"] = last_updated_by
|
|
339
339
|
|
|
340
|
-
if instance
|
|
340
|
+
if getattr(instance, "is_contact_associable_model", False):
|
|
341
341
|
paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
342
342
|
associations = instance.associated_contacts.restrict(request.user, "view").order_by("role__name")
|
|
343
343
|
associations_table = AssociatedContactsTable(associations, orderable=False)
|
|
@@ -347,7 +347,7 @@ def common_detail_view_context(request, instance):
|
|
|
347
347
|
else:
|
|
348
348
|
context["associated_contacts_table"] = None
|
|
349
349
|
|
|
350
|
-
if instance
|
|
350
|
+
if getattr(instance, "is_dynamic_group_associable_model", False):
|
|
351
351
|
paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
352
352
|
dynamic_groups = instance.dynamic_groups.restrict(request.user, "view")
|
|
353
353
|
dynamic_groups_table = DynamicGroupTable(dynamic_groups, orderable=False)
|
|
@@ -358,7 +358,7 @@ def common_detail_view_context(request, instance):
|
|
|
358
358
|
else:
|
|
359
359
|
context["associated_dynamic_groups_table"] = None
|
|
360
360
|
|
|
361
|
-
if instance
|
|
361
|
+
if getattr(instance, "is_metadata_associable_model", False):
|
|
362
362
|
paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
363
363
|
object_metadata = instance.associated_object_metadata.restrict(request.user, "view").order_by(
|
|
364
364
|
"metadata_type", "scoped_fields"
|
nautobot/dcim/factory.py
CHANGED
|
@@ -110,7 +110,7 @@ NETWORK_DRIVERS = {
|
|
|
110
110
|
"Palo Alto": ["paloalto_panos"],
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
TIME_ZONES =
|
|
113
|
+
TIME_ZONES = sorted(timezone for timezone, _ in TimeZoneFormField().choices)
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
# Retrieve correct rack reservation units
|
|
@@ -296,7 +296,7 @@ class DeviceTypeFactory(PrimaryModelFactory):
|
|
|
296
296
|
while not unused_models:
|
|
297
297
|
unused_models = {f"{device_type} {count}" for device_type in device_types}.difference(current_models)
|
|
298
298
|
count += 1
|
|
299
|
-
return factory.random.randgen.choice(
|
|
299
|
+
return factory.random.randgen.choice(sorted(unused_models))
|
|
300
300
|
|
|
301
301
|
has_part_number = NautobotBoolIterator()
|
|
302
302
|
part_number = factory.Maybe("has_part_number", factory.Faker("ean", length=8), "")
|
|
@@ -784,7 +784,7 @@ class ModuleTypeFactory(PrimaryModelFactory):
|
|
|
784
784
|
while not unused_models:
|
|
785
785
|
unused_models = {f"{module_type} {count}" for module_type in module_types}.difference(current_models)
|
|
786
786
|
count += 1
|
|
787
|
-
return factory.random.randgen.choice(
|
|
787
|
+
return factory.random.randgen.choice(sorted(unused_models))
|
|
788
788
|
|
|
789
789
|
|
|
790
790
|
class ModuleFactory(PrimaryModelFactory):
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -396,7 +396,7 @@ class DeviceModuleConsolePortTable(ConsolePortTable):
|
|
|
396
396
|
"actions",
|
|
397
397
|
)
|
|
398
398
|
row_attrs = {
|
|
399
|
-
"
|
|
399
|
+
"class": cable_status_color_css,
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
|
|
@@ -460,7 +460,7 @@ class DeviceModuleConsoleServerPortTable(ConsoleServerPortTable):
|
|
|
460
460
|
"actions",
|
|
461
461
|
)
|
|
462
462
|
row_attrs = {
|
|
463
|
-
"
|
|
463
|
+
"class": cable_status_color_css,
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
|
|
@@ -535,7 +535,7 @@ class DeviceModulePowerPortTable(PowerPortTable):
|
|
|
535
535
|
"connection",
|
|
536
536
|
"actions",
|
|
537
537
|
)
|
|
538
|
-
row_attrs = {"
|
|
538
|
+
row_attrs = {"class": cable_status_color_css}
|
|
539
539
|
|
|
540
540
|
|
|
541
541
|
class PowerOutletTable(ModularDeviceComponentTable, PathEndpointTable):
|
|
@@ -613,7 +613,7 @@ class DeviceModulePowerOutletTable(PowerOutletTable):
|
|
|
613
613
|
"connection",
|
|
614
614
|
"actions",
|
|
615
615
|
)
|
|
616
|
-
row_attrs = {"
|
|
616
|
+
row_attrs = {"class": cable_status_color_css}
|
|
617
617
|
|
|
618
618
|
|
|
619
619
|
class BaseInterfaceTable(BaseTable):
|
|
@@ -739,7 +739,7 @@ class DeviceModuleInterfaceTable(InterfaceTable):
|
|
|
739
739
|
"actions",
|
|
740
740
|
]
|
|
741
741
|
row_attrs = {
|
|
742
|
-
"
|
|
742
|
+
"class": cable_status_color_css,
|
|
743
743
|
"data-name": lambda record: record.name,
|
|
744
744
|
}
|
|
745
745
|
|
|
@@ -815,7 +815,7 @@ class DeviceModuleFrontPortTable(FrontPortTable):
|
|
|
815
815
|
"cable_peer",
|
|
816
816
|
"actions",
|
|
817
817
|
)
|
|
818
|
-
row_attrs = {"
|
|
818
|
+
row_attrs = {"class": cable_status_color_css}
|
|
819
819
|
|
|
820
820
|
|
|
821
821
|
class RearPortTable(ModularDeviceComponentTable, CableTerminationTable):
|
|
@@ -874,7 +874,7 @@ class DeviceModuleRearPortTable(RearPortTable):
|
|
|
874
874
|
"cable_peer",
|
|
875
875
|
"actions",
|
|
876
876
|
)
|
|
877
|
-
row_attrs = {"
|
|
877
|
+
row_attrs = {"class": cable_status_color_css}
|
|
878
878
|
|
|
879
879
|
|
|
880
880
|
class DeviceBayTable(DeviceComponentTable):
|
|
@@ -406,6 +406,18 @@
|
|
|
406
406
|
{% endif %}
|
|
407
407
|
{% if object.is_dynamic_group_associable_model and perms.extras.view_dynamicgroup %}
|
|
408
408
|
<div id="dynamic_groups" role="tabpanel" class="tab-pane {% if request.GET.tab == 'dynamic_groups' %}active{% else %}fade{% endif %}">
|
|
409
|
+
<div class="row">
|
|
410
|
+
<div class="col-md-12">
|
|
411
|
+
<div class="alert alert-warning">
|
|
412
|
+
Dynamic group membership is cached for performance reasons,
|
|
413
|
+
therefore this table may not always be up-to-date.
|
|
414
|
+
<br>You can refresh the membership of any specific group by navigating to it from the list below
|
|
415
|
+
or from the <a href="{% url 'extras:dynamicgroup_list' %}">Dynamic Groups list view</a>.
|
|
416
|
+
<br>You can also refresh the membership of all groups by running the
|
|
417
|
+
<a href="{% url 'extras:job_run_by_class_path' class_path='nautobot.core.jobs.groups.RefreshDynamicGroupCaches' %}">Refresh Dynamic Group Caches job</a>.
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
409
421
|
<div class="row">
|
|
410
422
|
<div class="col-md-12">
|
|
411
423
|
<form method="post">
|
|
@@ -215,6 +215,18 @@
|
|
|
215
215
|
{% endif %}
|
|
216
216
|
{% if object.is_dynamic_group_associable_model and perms.extras.view_dynamicgroup %}
|
|
217
217
|
<div id="dynamic_groups" role="tabpanel" class="tab-pane {% if request.GET.tab == 'dynamic_groups' %}active{% else %}fade{% endif %}">
|
|
218
|
+
<div class="row">
|
|
219
|
+
<div class="col-md-12">
|
|
220
|
+
<div class="alert alert-warning">
|
|
221
|
+
Dynamic group membership is cached for performance reasons,
|
|
222
|
+
therefore this table may not always be up-to-date.
|
|
223
|
+
<br>You can refresh the membership of any specific group by navigating to it from the list below
|
|
224
|
+
or from the <a href="{% url 'extras:dynamicgroup_list' %}">Dynamic Groups list view</a>.
|
|
225
|
+
<br>You can also refresh the membership of all groups by running the
|
|
226
|
+
<a href="{% url 'extras:job_run_by_class_path' class_path='nautobot.core.jobs.groups.RefreshDynamicGroupCaches' %}">Refresh Dynamic Group Caches job</a>.
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
218
230
|
<div class="row">
|
|
219
231
|
<div class="col-md-12">
|
|
220
232
|
<form method="post">
|
nautobot/dcim/utils.py
CHANGED
|
@@ -14,7 +14,7 @@ from netutils.lib_mapper import (
|
|
|
14
14
|
SCRAPLI_LIB_MAPPER_REVERSE,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
from nautobot.core.
|
|
17
|
+
from nautobot.core.choices import ColorChoices
|
|
18
18
|
from nautobot.core.utils.config import get_settings_or_config
|
|
19
19
|
from nautobot.dcim.choices import InterfaceModeChoices
|
|
20
20
|
from nautobot.dcim.constants import NETUTILS_NETWORK_DRIVER_MAPPING_NAMES
|
|
@@ -55,11 +55,14 @@ def cable_status_color_css(record):
|
|
|
55
55
|
"""
|
|
56
56
|
if not record.cable:
|
|
57
57
|
return ""
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
else:
|
|
59
|
+
CABLE_STATUS_TO_CSS_CLASS = {
|
|
60
|
+
ColorChoices.COLOR_GREEN: "success",
|
|
61
|
+
ColorChoices.COLOR_AMBER: "warning",
|
|
62
|
+
ColorChoices.COLOR_CYAN: "info",
|
|
63
|
+
}
|
|
64
|
+
status_color = record.cable.get_status_color().strip("#")
|
|
65
|
+
return CABLE_STATUS_TO_CSS_CLASS.get(status_color, "")
|
|
63
66
|
|
|
64
67
|
|
|
65
68
|
def get_network_driver_mapping_tool_names():
|
|
@@ -6,6 +6,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
|
|
6
6
|
from drf_spectacular.utils import extend_schema_field
|
|
7
7
|
from rest_framework import serializers
|
|
8
8
|
from rest_framework.validators import UniqueTogetherValidator
|
|
9
|
+
from timezone_field.rest_framework import TimeZoneSerializerField
|
|
9
10
|
|
|
10
11
|
from nautobot.core.api import (
|
|
11
12
|
BaseModelSerializer,
|
|
@@ -581,6 +582,7 @@ class JobVariableSerializer(serializers.Serializer):
|
|
|
581
582
|
|
|
582
583
|
class ScheduledJobSerializer(BaseModelSerializer):
|
|
583
584
|
# start_time = serializers.DateTimeField(format=None, required=False)
|
|
585
|
+
time_zone = TimeZoneSerializerField(required=False)
|
|
584
586
|
|
|
585
587
|
class Meta:
|
|
586
588
|
model = ScheduledJob
|
|
@@ -175,11 +175,12 @@ def web_request_context(
|
|
|
175
175
|
:param user: User object
|
|
176
176
|
:param context_detail: Optional extra details about the transaction (ex: the plugin name that initiated the change)
|
|
177
177
|
:param change_id: Optional uuid object to uniquely identify the transaction. One will be generated if not supplied
|
|
178
|
-
:param context: Optional string value of the generated change log entries' "change_context" field
|
|
179
|
-
|
|
178
|
+
:param context: Optional string value of the generated change log entries' "change_context" field.
|
|
179
|
+
Defaults to `ObjectChangeEventContextChoices.CONTEXT_ORM`.
|
|
180
|
+
Valid choices are in `nautobot.extras.choices.ObjectChangeEventContextChoices`.
|
|
180
181
|
:param request: Optional web request instance, one will be generated if not supplied
|
|
181
182
|
"""
|
|
182
|
-
from nautobot.extras.jobs import enqueue_job_hooks # prevent circular import
|
|
183
|
+
from nautobot.extras.jobs import enqueue_job_hooks, get_jobs # prevent circular import
|
|
183
184
|
|
|
184
185
|
valid_contexts = {
|
|
185
186
|
ObjectChangeEventContextChoices.CONTEXT_JOB: JobChangeContext,
|
|
@@ -202,9 +203,15 @@ def web_request_context(
|
|
|
202
203
|
with change_logging(change_context):
|
|
203
204
|
yield request
|
|
204
205
|
finally:
|
|
206
|
+
jobs_refreshed = False
|
|
205
207
|
# enqueue jobhooks and webhooks, use change_context.change_id in case change_id was not supplied
|
|
206
208
|
for object_change in ObjectChange.objects.filter(request_id=change_context.change_id).iterator():
|
|
207
|
-
|
|
209
|
+
if context != ObjectChangeEventContextChoices.CONTEXT_JOB_HOOK:
|
|
210
|
+
# Make sure JobHooks are up to date (once) before calling them
|
|
211
|
+
if not jobs_refreshed:
|
|
212
|
+
get_jobs(reload=True)
|
|
213
|
+
jobs_refreshed = True
|
|
214
|
+
enqueue_job_hooks(object_change)
|
|
208
215
|
enqueue_webhooks(object_change)
|
|
209
216
|
|
|
210
217
|
|
|
@@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
6
6
|
from django.db.models import Q
|
|
7
7
|
import django_filters
|
|
8
8
|
from drf_spectacular.utils import extend_schema_field
|
|
9
|
+
from timezone_field import TimeZoneField
|
|
9
10
|
|
|
10
11
|
from nautobot.core.api.exceptions import SerializerNotFound
|
|
11
12
|
from nautobot.core.api.utils import get_serializer_for_model
|
|
@@ -911,6 +912,7 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
911
912
|
"job_model__name": "icontains",
|
|
912
913
|
"name": "icontains",
|
|
913
914
|
"user__username": "icontains",
|
|
915
|
+
"scheduled_job__name": "icontains",
|
|
914
916
|
},
|
|
915
917
|
)
|
|
916
918
|
job_model = NaturalKeyOrPKMultipleChoiceFilter(
|
|
@@ -922,11 +924,16 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
|
|
|
922
924
|
queryset=Job.objects.all(),
|
|
923
925
|
label="Job (ID) - Deprecated (use job_model filter)",
|
|
924
926
|
)
|
|
927
|
+
scheduled_job = NaturalKeyOrPKMultipleChoiceFilter(
|
|
928
|
+
to_field_name="name",
|
|
929
|
+
queryset=ScheduledJob.objects.all(),
|
|
930
|
+
label="Scheduled Job (name or ID)",
|
|
931
|
+
)
|
|
925
932
|
status = django_filters.MultipleChoiceFilter(choices=JobResultStatusChoices, null_value=None)
|
|
926
933
|
|
|
927
934
|
class Meta:
|
|
928
935
|
model = JobResult
|
|
929
|
-
fields = ["id", "date_created", "date_done", "name", "status", "user"]
|
|
936
|
+
fields = ["id", "date_created", "date_done", "name", "status", "user", "scheduled_job"]
|
|
930
937
|
|
|
931
938
|
|
|
932
939
|
class JobLogEntryFilterSet(BaseFilterSet):
|
|
@@ -960,10 +967,15 @@ class ScheduledJobFilterSet(BaseFilterSet):
|
|
|
960
967
|
queryset=Job.objects.all(),
|
|
961
968
|
label="Job (ID) - Deprecated (use job_model filter)",
|
|
962
969
|
)
|
|
970
|
+
time_zone = django_filters.MultipleChoiceFilter(
|
|
971
|
+
choices=[(str(obj), name) for obj, name in TimeZoneField().choices],
|
|
972
|
+
label="Time zone",
|
|
973
|
+
null_value="",
|
|
974
|
+
)
|
|
963
975
|
|
|
964
976
|
class Meta:
|
|
965
977
|
model = ScheduledJob
|
|
966
|
-
fields = ["id", "name", "total_run_count", "start_time", "last_run_at"]
|
|
978
|
+
fields = ["id", "name", "total_run_count", "start_time", "last_run_at", "time_zone"]
|
|
967
979
|
|
|
968
980
|
|
|
969
981
|
#
|
nautobot/extras/forms/forms.py
CHANGED
|
@@ -1286,6 +1286,12 @@ class JobResultFilterForm(BootstrapMixin, forms.Form):
|
|
|
1286
1286
|
required=False,
|
|
1287
1287
|
widget=StaticSelect2Multiple(),
|
|
1288
1288
|
)
|
|
1289
|
+
scheduled_job = DynamicModelMultipleChoiceField(
|
|
1290
|
+
label="Scheduled Job",
|
|
1291
|
+
queryset=ScheduledJob.objects.all(),
|
|
1292
|
+
required=False,
|
|
1293
|
+
to_field_name="name",
|
|
1294
|
+
)
|
|
1289
1295
|
|
|
1290
1296
|
|
|
1291
1297
|
class ScheduledJobFilterForm(BootstrapMixin, forms.Form):
|
nautobot/extras/forms/mixins.py
CHANGED
|
@@ -174,7 +174,7 @@ class DynamicGroupModelFormMixin(forms.ModelForm):
|
|
|
174
174
|
|
|
175
175
|
def __init__(self, *args, **kwargs):
|
|
176
176
|
super().__init__(*args, **kwargs)
|
|
177
|
-
if self._meta.model
|
|
177
|
+
if getattr(self._meta.model, "is_dynamic_group_associable_model", False):
|
|
178
178
|
self.fields["dynamic_groups"] = DynamicModelMultipleChoiceField(
|
|
179
179
|
required=False,
|
|
180
180
|
initial=self.instance.dynamic_groups if self.instance else None,
|
|
@@ -193,7 +193,7 @@ class DynamicGroupModelFormMixin(forms.ModelForm):
|
|
|
193
193
|
|
|
194
194
|
def save(self, commit=True):
|
|
195
195
|
obj = super().save(commit=commit)
|
|
196
|
-
if commit and obj
|
|
196
|
+
if commit and getattr(obj, "is_dynamic_group_associable_model", False):
|
|
197
197
|
current_groups = set(obj.dynamic_groups.filter(group_type=DynamicGroupTypeChoices.TYPE_STATIC))
|
|
198
198
|
for dynamic_group in set(self.cleaned_data.get("dynamic_groups")).difference(current_groups):
|
|
199
199
|
dynamic_group.add_members([obj])
|
nautobot/extras/jobs.py
CHANGED
|
@@ -1168,7 +1168,6 @@ def enqueue_job_hooks(object_change):
|
|
|
1168
1168
|
job_hooks = JobHook.objects.filter(content_types=content_type, enabled=True, **{action_flag: True})
|
|
1169
1169
|
|
|
1170
1170
|
# Enqueue the jobs related to the job_hooks
|
|
1171
|
-
get_jobs(reload=True)
|
|
1172
1171
|
for job_hook in job_hooks:
|
|
1173
1172
|
job_model = job_hook.job
|
|
1174
1173
|
if not job_model.installed or not job_model.enabled:
|
|
@@ -32,6 +32,7 @@ STATUS_CHOICESET_MAP = {
|
|
|
32
32
|
"ipam.IPAddress": ipam_choices.IPAddressStatusChoices,
|
|
33
33
|
"ipam.Prefix": ipam_choices.PrefixStatusChoices,
|
|
34
34
|
"ipam.VLAN": ipam_choices.VLANStatusChoices,
|
|
35
|
+
"ipam.VRF": ipam_choices.VRFStatusChoices,
|
|
35
36
|
"virtualization.VirtualMachine": vm_choices.VirtualMachineStatusChoices,
|
|
36
37
|
"virtualization.VMInterface": vm_choices.VMInterfaceStatusChoices,
|
|
37
38
|
}
|
|
@@ -48,6 +49,7 @@ STATUS_COLOR_MAP = {
|
|
|
48
49
|
"Decommissioning": ColorChoices.COLOR_AMBER,
|
|
49
50
|
"Deprecated": ColorChoices.COLOR_RED,
|
|
50
51
|
"Deprovisioning": ColorChoices.COLOR_AMBER,
|
|
52
|
+
"Down": ColorChoices.COLOR_AMBER,
|
|
51
53
|
"End-of-Life": ColorChoices.COLOR_RED,
|
|
52
54
|
"Extended Support": ColorChoices.COLOR_CYAN,
|
|
53
55
|
"Failed": ColorChoices.COLOR_RED,
|
|
@@ -76,6 +78,7 @@ STATUS_DESCRIPTION_MAP = {
|
|
|
76
78
|
"Decommissioning": "Unit is being decommissioned",
|
|
77
79
|
"Deprecated": "Unit has been deprecated",
|
|
78
80
|
"Deprovisioning": "Circuit is being deprovisioned",
|
|
81
|
+
"Down": "VRF is down",
|
|
79
82
|
"End-of-Life": "Unit has reached end-of-life",
|
|
80
83
|
"Extended Support": "Software is in extended support",
|
|
81
84
|
"Failed": "Unit has failed",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 4.2.15 on 2024-08-19 13:44
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
from django.utils import timezone
|
|
5
|
+
import timezone_field.fields
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
dependencies = [
|
|
10
|
+
("extras", "0114_computedfield_grouping"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="scheduledjob",
|
|
16
|
+
name="time_zone",
|
|
17
|
+
field=timezone_field.fields.TimeZoneField(default=timezone.get_default_timezone_name),
|
|
18
|
+
),
|
|
19
|
+
migrations.AlterModelOptions(
|
|
20
|
+
name="scheduledjob",
|
|
21
|
+
options={"ordering": ["name"]},
|
|
22
|
+
),
|
|
23
|
+
]
|
nautobot/extras/models/groups.py
CHANGED
|
@@ -251,7 +251,10 @@ class DynamicGroup(PrimaryModel):
|
|
|
251
251
|
# Skip filter fields that have methods defined. They are not reversible.
|
|
252
252
|
if skip_method_filters and filterset_field.method is not None:
|
|
253
253
|
# Don't skip method fields that also have a "generate_query_" method
|
|
254
|
-
|
|
254
|
+
query_attr = (
|
|
255
|
+
filterset_field.method.__name__ if callable(filterset_field.method) else filterset_field.method
|
|
256
|
+
)
|
|
257
|
+
if hasattr(filterset, f"generate_query_{query_attr}"):
|
|
255
258
|
logger.debug(
|
|
256
259
|
"Keeping %s for filterform: has a `generate_query_` filter method", filterset_field_name
|
|
257
260
|
)
|