nautobot 2.4.11__py3-none-any.whl → 2.4.13__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/circuits/templates/circuits/circuittermination_retrieve.html +1 -114
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +1 -1
- nautobot/circuits/views.py +76 -6
- nautobot/cloud/navigation.py +1 -1
- nautobot/core/jobs/cleanup.py +4 -4
- nautobot/core/templates/components/panel/header_extra_content_table.html +12 -1
- nautobot/core/templates/components/panel/panel.html +4 -0
- nautobot/core/templates/generic/object_retrieve.html +1 -0
- nautobot/core/templates/inc/footer.html +4 -4
- nautobot/core/tests/test_utils.py +1 -177
- nautobot/core/tests/test_views.py +2 -2
- nautobot/core/ui/object_detail.py +1 -1
- nautobot/core/utils/module_loading.py +58 -63
- nautobot/dcim/filters/__init__.py +6 -0
- nautobot/dcim/filters/mixins.py +2 -1
- nautobot/dcim/migrations/0071_alter_consoleport_options_and_more.py +42 -0
- nautobot/dcim/models/device_components.py +10 -2
- nautobot/dcim/models/devices.py +28 -0
- nautobot/dcim/tables/devices.py +2 -0
- nautobot/dcim/templates/dcim/controller/base.html +1 -9
- nautobot/dcim/templates/dcim/controller_retrieve.html +2 -83
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +2 -25
- nautobot/dcim/templates/dcim/device.html +1 -34
- nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -1
- nautobot/dcim/templates/dcim/virtualchassis.html +2 -51
- nautobot/dcim/templates/dcim/virtualchassis_add.html +2 -22
- nautobot/dcim/templates/dcim/virtualchassis_create.html +22 -0
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +2 -93
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +51 -0
- nautobot/dcim/templates/dcim/virtualchassis_update.html +93 -0
- nautobot/dcim/tests/test_models.py +1 -0
- nautobot/dcim/urls.py +2 -59
- nautobot/dcim/views.py +180 -191
- nautobot/extras/datasources/git.py +2 -2
- nautobot/extras/jobs_ui.py +208 -0
- nautobot/extras/models/tags.py +4 -0
- nautobot/extras/tables.py +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +2 -2
- nautobot/extras/templates/extras/inc/configcontext_data.html +1 -1
- nautobot/extras/templates/extras/inc/configcontext_format.html +6 -0
- nautobot/extras/templates/extras/inc/json_data.html +1 -1
- nautobot/extras/templates/extras/inc/json_format.html +2 -2
- nautobot/extras/templates/extras/job_detail.html +1 -326
- nautobot/extras/templates/extras/tag_retrieve.html +1 -51
- nautobot/extras/views.py +83 -29
- nautobot/ipam/migrations/0052_alter_ipaddress_index_together_and_more.py +28 -0
- nautobot/ipam/models.py +13 -1
- nautobot/ipam/tests/test_api.py +1 -3
- nautobot/ipam/utils/testing.py +76 -29
- nautobot/project-static/docs/404.html +2 -2
- nautobot/project-static/docs/apps/index.html +2 -2
- nautobot/project-static/docs/apps/nautobot-apps.html +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js → bundle.56ea9cef.min.js} +2 -2
- nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js.map → bundle.56ea9cef.min.js.map} +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2 -2
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +4 -2
- nautobot/project-static/docs/development/apps/api/configuration-view.html +2 -2
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +2 -2
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +2 -2
- nautobot/project-static/docs/development/apps/api/models/global-search.html +2 -2
- nautobot/project-static/docs/development/apps/api/models/graphql.html +2 -2
- nautobot/project-static/docs/development/apps/api/models/index.html +2 -2
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +2 -2
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +2 -2
- nautobot/project-static/docs/development/apps/api/prometheus.html +2 -2
- nautobot/project-static/docs/development/apps/api/setup.html +2 -2
- nautobot/project-static/docs/development/apps/api/testing.html +2 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +2 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +2 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +2 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +2 -2
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/base-template.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/index.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/notes.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +2 -2
- nautobot/project-static/docs/development/apps/api/views/urls.html +2 -2
- nautobot/project-static/docs/development/apps/index.html +2 -2
- nautobot/project-static/docs/development/apps/migration/code-updates.html +2 -2
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +2 -2
- nautobot/project-static/docs/development/apps/migration/from-v1.html +2 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +2 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +2 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +2 -2
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +2 -2
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +2 -2
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +2 -2
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +2 -2
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +2 -2
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +2 -2
- nautobot/project-static/docs/development/core/application-registry.html +2 -2
- nautobot/project-static/docs/development/core/best-practices.html +2 -2
- nautobot/project-static/docs/development/core/bootstrap-ui.html +2 -2
- nautobot/project-static/docs/development/core/caching.html +2 -2
- nautobot/project-static/docs/development/core/controllers.html +2 -2
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +2 -2
- nautobot/project-static/docs/development/core/generic-views.html +2 -2
- nautobot/project-static/docs/development/core/getting-started.html +2 -2
- nautobot/project-static/docs/development/core/homepage.html +2 -2
- nautobot/project-static/docs/development/core/index.html +2 -2
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +2 -2
- nautobot/project-static/docs/development/core/model-checklist.html +2 -2
- nautobot/project-static/docs/development/core/model-features.html +2 -2
- nautobot/project-static/docs/development/core/natural-keys.html +2 -2
- nautobot/project-static/docs/development/core/navigation-menu.html +2 -2
- nautobot/project-static/docs/development/core/release-checklist.html +2 -2
- nautobot/project-static/docs/development/core/role-internals.html +2 -2
- nautobot/project-static/docs/development/core/settings.html +2 -2
- nautobot/project-static/docs/development/core/style-guide.html +2 -2
- nautobot/project-static/docs/development/core/templates.html +2 -2
- nautobot/project-static/docs/development/core/testing.html +2 -2
- nautobot/project-static/docs/development/core/ui-component-framework.html +2 -2
- nautobot/project-static/docs/development/core/user-preferences.html +2 -2
- nautobot/project-static/docs/development/index.html +2 -2
- nautobot/project-static/docs/development/jobs/getting-started.html +2 -6
- nautobot/project-static/docs/development/jobs/index.html +2 -2
- nautobot/project-static/docs/development/jobs/installation.html +2 -2
- nautobot/project-static/docs/development/jobs/job-extensions.html +2 -2
- nautobot/project-static/docs/development/jobs/job-logging.html +2 -2
- nautobot/project-static/docs/development/jobs/job-patterns.html +2 -2
- nautobot/project-static/docs/development/jobs/job-structure.html +2 -2
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +2 -2
- nautobot/project-static/docs/development/jobs/testing.html +2 -2
- nautobot/project-static/docs/index.html +2 -2
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +2 -2
- nautobot/project-static/docs/overview/design_philosophy.html +2 -2
- nautobot/project-static/docs/release-notes/index.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.0.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.1.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.2.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.3.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.4.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.5.html +2 -2
- nautobot/project-static/docs/release-notes/version-1.6.html +2 -2
- nautobot/project-static/docs/release-notes/version-2.0.html +2 -2
- nautobot/project-static/docs/release-notes/version-2.1.html +2 -2
- nautobot/project-static/docs/release-notes/version-2.2.html +2 -2
- nautobot/project-static/docs/release-notes/version-2.3.html +2 -2
- nautobot/project-static/docs/release-notes/version-2.4.html +211 -2
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +299 -299
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +2 -2
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +2 -2
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +2 -2
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +2 -2
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +2 -2
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -2
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +2 -2
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/index.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +2 -2
- nautobot/project-static/docs/user-guide/administration/installation/services.html +2 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +2 -2
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +2 -2
- nautobot/project-static/docs/user-guide/administration/security/index.html +2 -2
- nautobot/project-static/docs/user-guide/administration/security/notices.html +2 -2
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +2 -2
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +2 -2
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +2 -2
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +2 -2
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +2 -2
- nautobot/project-static/docs/user-guide/index.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +2 -2
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +2 -2
- nautobot/wireless/navigation.py +1 -1
- nautobot/wireless/tables.py +13 -5
- {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/METADATA +1 -1
- {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/RECORD +364 -357
- {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/NOTICE +0 -0
- {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/WHEEL +0 -0
- {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py
CHANGED
|
@@ -59,6 +59,7 @@ from nautobot.core.views.viewsets import NautobotUIViewSet
|
|
|
59
59
|
from nautobot.dcim.choices import LocationDataToContactActionChoices
|
|
60
60
|
from nautobot.dcim.forms import LocationMigrateDataToContactForm
|
|
61
61
|
from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
|
|
62
|
+
from nautobot.extras.tables import DynamicGroupTable
|
|
62
63
|
from nautobot.extras.views import ObjectChangeLogView, ObjectConfigContextView, ObjectDynamicGroupsView
|
|
63
64
|
from nautobot.ipam.models import IPAddress, Prefix, Service, VLAN
|
|
64
65
|
from nautobot.ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable, VRFDeviceAssignmentTable, VRFTable
|
|
@@ -70,8 +71,8 @@ from nautobot.wireless.models import (
|
|
|
70
71
|
ControllerManagedDeviceGroupWirelessNetworkAssignment,
|
|
71
72
|
)
|
|
72
73
|
from nautobot.wireless.tables import (
|
|
74
|
+
BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
73
75
|
ControllerManagedDeviceGroupRadioProfileAssignmentTable,
|
|
74
|
-
ControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
75
76
|
DeviceGroupWirelessNetworkTable,
|
|
76
77
|
RadioProfileTable,
|
|
77
78
|
)
|
|
@@ -1701,7 +1702,39 @@ class DeviceView(generic.ObjectView):
|
|
|
1701
1702
|
"tenant__tenant_group",
|
|
1702
1703
|
).prefetch_related("images", "software_image_files")
|
|
1703
1704
|
|
|
1704
|
-
|
|
1705
|
+
class DeviceDetailContent(object_detail.ObjectDetailContent):
|
|
1706
|
+
"""
|
|
1707
|
+
Override base ObjectDetailContent to render dynamic-groups table as a separate view/tab instead of inline.
|
|
1708
|
+
"""
|
|
1709
|
+
|
|
1710
|
+
def __init__(self, **kwargs):
|
|
1711
|
+
super().__init__(**kwargs)
|
|
1712
|
+
# Remove inline tab definition
|
|
1713
|
+
for tab in list(self._tabs):
|
|
1714
|
+
if isinstance(tab, object_detail._ObjectDetailGroupsTab):
|
|
1715
|
+
self._tabs.remove(tab)
|
|
1716
|
+
# Add distinct-view tab definition
|
|
1717
|
+
self._tabs.append(
|
|
1718
|
+
object_detail.DistinctViewTab(
|
|
1719
|
+
weight=object_detail.Tab.WEIGHT_GROUPS_TAB,
|
|
1720
|
+
tab_id="dynamic_groups",
|
|
1721
|
+
label="Dynamic Groups",
|
|
1722
|
+
url_name="dcim:device_dynamicgroups",
|
|
1723
|
+
related_object_attribute="dynamic_groups",
|
|
1724
|
+
panels=(
|
|
1725
|
+
object_detail.ObjectsTablePanel(
|
|
1726
|
+
weight=100,
|
|
1727
|
+
table_class=DynamicGroupTable,
|
|
1728
|
+
table_attribute="dynamic_groups",
|
|
1729
|
+
exclude_columns=["content_type"],
|
|
1730
|
+
add_button_route=None,
|
|
1731
|
+
related_field_name="member_id",
|
|
1732
|
+
),
|
|
1733
|
+
),
|
|
1734
|
+
)
|
|
1735
|
+
)
|
|
1736
|
+
|
|
1737
|
+
object_detail_content = DeviceDetailContent(
|
|
1705
1738
|
extra_buttons=(
|
|
1706
1739
|
object_detail.DropdownButton(
|
|
1707
1740
|
weight=100,
|
|
@@ -2188,7 +2221,7 @@ class DeviceChangeLogView(ObjectChangeLogView):
|
|
|
2188
2221
|
base_template = "dcim/device/base.html"
|
|
2189
2222
|
|
|
2190
2223
|
|
|
2191
|
-
class DeviceDynamicGroupsView(ObjectDynamicGroupsView):
|
|
2224
|
+
class DeviceDynamicGroupsView(ObjectDynamicGroupsView):
|
|
2192
2225
|
base_template = "dcim/device/base.html"
|
|
2193
2226
|
|
|
2194
2227
|
|
|
@@ -2238,7 +2271,7 @@ class DeviceWirelessView(generic.ObjectView):
|
|
|
2238
2271
|
wireless_networks = ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.filter(
|
|
2239
2272
|
controller_managed_device_group=controller_managed_device_group
|
|
2240
2273
|
).select_related("wireless_network", "controller_managed_device_group", "vlan")
|
|
2241
|
-
wireless_networks_table =
|
|
2274
|
+
wireless_networks_table = BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable(
|
|
2242
2275
|
data=wireless_networks, user=request.user, orderable=False
|
|
2243
2276
|
)
|
|
2244
2277
|
wireless_networks_table.columns.hide("controller_managed_device_group")
|
|
@@ -3907,151 +3940,102 @@ class InterfaceConnectionsListView(ConnectionsListView):
|
|
|
3907
3940
|
#
|
|
3908
3941
|
|
|
3909
3942
|
|
|
3910
|
-
class
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
class VirtualChassisView(generic.ObjectView):
|
|
3943
|
+
class VirtualChassisUIViewSet(NautobotUIViewSet):
|
|
3944
|
+
bulk_update_form_class = forms.VirtualChassisBulkEditForm
|
|
3945
|
+
filterset_class = filters.VirtualChassisFilterSet
|
|
3946
|
+
filterset_form_class = forms.VirtualChassisFilterForm
|
|
3947
|
+
form_class = forms.VirtualChassisCreateForm
|
|
3948
|
+
serializer_class = serializers.VirtualChassisSerializer
|
|
3949
|
+
table_class = tables.VirtualChassisTable
|
|
3918
3950
|
queryset = VirtualChassis.objects.all()
|
|
3919
3951
|
|
|
3920
3952
|
def get_extra_context(self, request, instance):
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
return {"members": members, **super().get_extra_context(request, instance)}
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
class VirtualChassisCreateView(generic.ObjectEditView):
|
|
3927
|
-
queryset = VirtualChassis.objects.all()
|
|
3928
|
-
model_form = forms.VirtualChassisCreateForm
|
|
3929
|
-
template_name = "dcim/virtualchassis_add.html"
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
|
|
3933
|
-
queryset = VirtualChassis.objects.all()
|
|
3934
|
-
|
|
3935
|
-
def get_required_permission(self):
|
|
3936
|
-
return "dcim.change_virtualchassis"
|
|
3937
|
-
|
|
3938
|
-
def get(self, request, pk):
|
|
3939
|
-
virtual_chassis = get_object_or_404(self.queryset, pk=pk)
|
|
3940
|
-
VCMemberFormSet = modelformset_factory(
|
|
3941
|
-
model=Device,
|
|
3942
|
-
form=forms.DeviceVCMembershipForm,
|
|
3943
|
-
formset=forms.BaseVCMemberFormSet,
|
|
3944
|
-
extra=0,
|
|
3945
|
-
)
|
|
3946
|
-
members_queryset = virtual_chassis.members.select_related("rack").order_by("vc_position")
|
|
3947
|
-
|
|
3948
|
-
vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
|
|
3949
|
-
vc_form.fields["master"].queryset = members_queryset
|
|
3950
|
-
formset = VCMemberFormSet(queryset=members_queryset)
|
|
3951
|
-
|
|
3952
|
-
return render(
|
|
3953
|
-
request,
|
|
3954
|
-
"dcim/virtualchassis_edit.html",
|
|
3955
|
-
{
|
|
3956
|
-
"vc_form": vc_form,
|
|
3957
|
-
"formset": formset,
|
|
3958
|
-
"return_url": self.get_return_url(request, virtual_chassis),
|
|
3959
|
-
},
|
|
3960
|
-
)
|
|
3961
|
-
|
|
3962
|
-
def post(self, request, pk):
|
|
3963
|
-
virtual_chassis = get_object_or_404(self.queryset, pk=pk)
|
|
3964
|
-
VCMemberFormSet = modelformset_factory(
|
|
3965
|
-
model=Device,
|
|
3966
|
-
form=forms.DeviceVCMembershipForm,
|
|
3967
|
-
formset=forms.BaseVCMemberFormSet,
|
|
3968
|
-
extra=0,
|
|
3969
|
-
)
|
|
3970
|
-
members_queryset = virtual_chassis.members.select_related("rack").order_by("vc_position")
|
|
3971
|
-
|
|
3972
|
-
vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
|
|
3973
|
-
vc_form.fields["master"].queryset = members_queryset
|
|
3974
|
-
formset = VCMemberFormSet(request.POST, queryset=members_queryset)
|
|
3975
|
-
|
|
3976
|
-
if vc_form.is_valid() and formset.is_valid():
|
|
3977
|
-
with transaction.atomic():
|
|
3978
|
-
# Save the VirtualChassis
|
|
3979
|
-
vc_form.save()
|
|
3980
|
-
|
|
3981
|
-
# Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
|
|
3982
|
-
# duplicate positions. Then save each member instance.
|
|
3983
|
-
members = formset.save(commit=False)
|
|
3984
|
-
devices = Device.objects.filter(pk__in=[m.pk for m in members])
|
|
3985
|
-
for device in devices:
|
|
3986
|
-
device.vc_position = None
|
|
3987
|
-
device.save()
|
|
3988
|
-
for member in members:
|
|
3989
|
-
member.save()
|
|
3990
|
-
|
|
3991
|
-
return redirect(virtual_chassis.get_absolute_url())
|
|
3992
|
-
|
|
3993
|
-
return render(
|
|
3994
|
-
request,
|
|
3995
|
-
"dcim/virtualchassis_edit.html",
|
|
3996
|
-
{
|
|
3997
|
-
"vc_form": vc_form,
|
|
3998
|
-
"formset": formset,
|
|
3999
|
-
"return_url": self.get_return_url(request, virtual_chassis),
|
|
4000
|
-
},
|
|
4001
|
-
)
|
|
4002
|
-
|
|
3953
|
+
context = super().get_extra_context(request, instance)
|
|
4003
3954
|
|
|
4004
|
-
|
|
4005
|
-
|
|
3955
|
+
if self.action == "update":
|
|
3956
|
+
VCMemberFormSet = modelformset_factory(
|
|
3957
|
+
model=Device,
|
|
3958
|
+
form=forms.DeviceVCMembershipForm,
|
|
3959
|
+
formset=forms.BaseVCMemberFormSet,
|
|
3960
|
+
extra=0,
|
|
3961
|
+
)
|
|
3962
|
+
members_queryset = instance.members.select_related("rack").order_by("vc_position")
|
|
4006
3963
|
|
|
3964
|
+
if request.method == "POST":
|
|
3965
|
+
formset = VCMemberFormSet(request.POST, queryset=members_queryset)
|
|
3966
|
+
else:
|
|
3967
|
+
formset = VCMemberFormSet(queryset=members_queryset)
|
|
4007
3968
|
|
|
4008
|
-
|
|
4009
|
-
|
|
3969
|
+
vc_form = forms.VirtualChassisForm(instance=instance)
|
|
3970
|
+
vc_form.fields["master"].queryset = members_queryset
|
|
4010
3971
|
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
3972
|
+
context.update(
|
|
3973
|
+
{
|
|
3974
|
+
"formset": formset,
|
|
3975
|
+
"vc_form": vc_form,
|
|
3976
|
+
"return_url": self.get_return_url(request, instance),
|
|
3977
|
+
}
|
|
3978
|
+
)
|
|
4016
3979
|
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
3980
|
+
elif self.action == "retrieve":
|
|
3981
|
+
members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
|
|
3982
|
+
context.update({"members": members})
|
|
4020
3983
|
|
|
4021
|
-
return
|
|
4022
|
-
request,
|
|
4023
|
-
"dcim/virtualchassis_add_member.html",
|
|
4024
|
-
{
|
|
4025
|
-
"virtual_chassis": virtual_chassis,
|
|
4026
|
-
"member_select_form": member_select_form,
|
|
4027
|
-
"membership_form": membership_form,
|
|
4028
|
-
"return_url": self.get_return_url(request, virtual_chassis),
|
|
4029
|
-
},
|
|
4030
|
-
)
|
|
3984
|
+
return context
|
|
4031
3985
|
|
|
4032
|
-
def
|
|
4033
|
-
|
|
3986
|
+
def form_save(self, form, **kwargs):
|
|
3987
|
+
obj = super().form_save(form, **kwargs)
|
|
4034
3988
|
|
|
4035
|
-
|
|
3989
|
+
if self.action == "update":
|
|
3990
|
+
context = self.get_extra_context(self.request, obj)
|
|
3991
|
+
formset = context.get("formset")
|
|
4036
3992
|
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
3993
|
+
if formset.is_valid():
|
|
3994
|
+
with transaction.atomic():
|
|
3995
|
+
members = formset.save(commit=False)
|
|
3996
|
+
# Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
|
|
3997
|
+
# duplicate positions. Then save each member instance.
|
|
3998
|
+
Device.objects.filter(pk__in=[m.pk for m in members]).update(vc_position=None)
|
|
3999
|
+
for member in members:
|
|
4000
|
+
member.save()
|
|
4001
|
+
else:
|
|
4002
|
+
raise ValidationError(formset.errors)
|
|
4042
4003
|
|
|
4043
|
-
|
|
4044
|
-
membership_form.save()
|
|
4045
|
-
msg = format_html('Added member <a href="{}">{}</a>', device.get_absolute_url(), device)
|
|
4046
|
-
messages.success(request, msg)
|
|
4004
|
+
return obj
|
|
4047
4005
|
|
|
4048
|
-
|
|
4049
|
-
|
|
4006
|
+
@action(
|
|
4007
|
+
detail=True,
|
|
4008
|
+
methods=["get", "post"],
|
|
4009
|
+
url_path="add-member",
|
|
4010
|
+
url_name="add_member",
|
|
4011
|
+
custom_view_base_action="change",
|
|
4012
|
+
custom_view_additional_permissions=["dcim.change_virtualchassis"],
|
|
4013
|
+
)
|
|
4014
|
+
def add_member(self, request, pk=None):
|
|
4015
|
+
virtual_chassis = self.get_object()
|
|
4050
4016
|
|
|
4051
|
-
|
|
4017
|
+
if request.method == "POST":
|
|
4018
|
+
member_select_form = forms.VCMemberSelectForm(request.POST)
|
|
4019
|
+
if member_select_form.is_valid():
|
|
4020
|
+
device = member_select_form.cleaned_data["device"]
|
|
4021
|
+
device.virtual_chassis = virtual_chassis
|
|
4022
|
+
data = {k: request.POST[k] for k in ["vc_position", "vc_priority"]}
|
|
4023
|
+
membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
|
|
4024
|
+
|
|
4025
|
+
if membership_form.is_valid():
|
|
4026
|
+
membership_form.save()
|
|
4027
|
+
msg = format_html('Added member <a href="{}">{}</a>', device.get_absolute_url(), device)
|
|
4028
|
+
messages.success(request, msg)
|
|
4052
4029
|
|
|
4030
|
+
if "_addanother" in request.POST:
|
|
4031
|
+
return redirect(request.get_full_path())
|
|
4032
|
+
return redirect(self.get_return_url(request, device))
|
|
4033
|
+
else:
|
|
4034
|
+
membership_form = forms.DeviceVCMembershipForm(data=request.POST)
|
|
4053
4035
|
else:
|
|
4054
|
-
|
|
4036
|
+
initial_data = {k: request.GET[k] for k in request.GET}
|
|
4037
|
+
member_select_form = forms.VCMemberSelectForm(initial=initial_data)
|
|
4038
|
+
membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
|
|
4055
4039
|
|
|
4056
4040
|
return render(
|
|
4057
4041
|
request,
|
|
@@ -4120,24 +4104,6 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
|
|
|
4120
4104
|
)
|
|
4121
4105
|
|
|
4122
4106
|
|
|
4123
|
-
class VirtualChassisBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
|
|
4124
|
-
queryset = VirtualChassis.objects.all()
|
|
4125
|
-
table = tables.VirtualChassisTable
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
class VirtualChassisBulkEditView(generic.BulkEditView):
|
|
4129
|
-
queryset = VirtualChassis.objects.all()
|
|
4130
|
-
filterset = filters.VirtualChassisFilterSet
|
|
4131
|
-
table = tables.VirtualChassisTable
|
|
4132
|
-
form = forms.VirtualChassisBulkEditForm
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
|
|
4136
|
-
queryset = VirtualChassis.objects.all()
|
|
4137
|
-
filterset = filters.VirtualChassisFilterSet
|
|
4138
|
-
table = tables.VirtualChassisTable
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
4107
|
#
|
|
4142
4108
|
# Power panels
|
|
4143
4109
|
#
|
|
@@ -4687,25 +4653,69 @@ class ControllerUIViewSet(NautobotUIViewSet):
|
|
|
4687
4653
|
table_class = tables.ControllerTable
|
|
4688
4654
|
template_name = "dcim/controller_create.html"
|
|
4689
4655
|
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4656
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
4657
|
+
panels=(
|
|
4658
|
+
object_detail.ObjectFieldsPanel(
|
|
4659
|
+
section=SectionChoices.LEFT_HALF,
|
|
4660
|
+
weight=100,
|
|
4661
|
+
fields="__all__",
|
|
4662
|
+
value_transforms={
|
|
4663
|
+
"capabilities": [helpers.label_list],
|
|
4664
|
+
},
|
|
4665
|
+
exclude_fields=[
|
|
4666
|
+
"external_integration",
|
|
4667
|
+
"controller_device",
|
|
4668
|
+
"controller_device_redundancy_group",
|
|
4669
|
+
],
|
|
4670
|
+
),
|
|
4671
|
+
object_detail.ObjectFieldsPanel(
|
|
4672
|
+
section=SectionChoices.RIGHT_HALF,
|
|
4673
|
+
weight=200,
|
|
4674
|
+
label="Integration",
|
|
4675
|
+
fields=[
|
|
4676
|
+
"external_integration",
|
|
4677
|
+
"controller_device",
|
|
4678
|
+
"controller_device_redundancy_group",
|
|
4679
|
+
],
|
|
4680
|
+
),
|
|
4681
|
+
object_detail.ObjectsTablePanel(
|
|
4682
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4683
|
+
weight=100,
|
|
4684
|
+
table_class=tables.DeviceTable,
|
|
4685
|
+
table_title="Managed Devices",
|
|
4686
|
+
table_filter="controller_managed_device_group__controller",
|
|
4687
|
+
include_columns=[
|
|
4688
|
+
"capabilities",
|
|
4689
|
+
"controller_managed_device_group",
|
|
4690
|
+
"manufacturer",
|
|
4691
|
+
],
|
|
4692
|
+
related_field_name="controller",
|
|
4693
|
+
add_button_route=None,
|
|
4694
|
+
),
|
|
4695
|
+
),
|
|
4696
|
+
extra_tabs=(
|
|
4697
|
+
object_detail.DistinctViewTab(
|
|
4698
|
+
weight=700,
|
|
4699
|
+
tab_id="wireless_networks",
|
|
4700
|
+
url_name="dcim:controller_wirelessnetworks",
|
|
4701
|
+
label="Wireless Networks",
|
|
4702
|
+
related_object_attribute="wireless_network_assignments",
|
|
4703
|
+
panels=(
|
|
4704
|
+
object_detail.ObjectsTablePanel(
|
|
4705
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4706
|
+
weight=100,
|
|
4707
|
+
table_title="Wireless Networks",
|
|
4708
|
+
table_class=BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
4709
|
+
table_filter="controller_managed_device_group__controller",
|
|
4710
|
+
tab_id="wireless_networks",
|
|
4711
|
+
add_button_route=None,
|
|
4712
|
+
select_related_fields=["wireless_network"],
|
|
4713
|
+
exclude_columns=["controller"],
|
|
4714
|
+
),
|
|
4715
|
+
),
|
|
4716
|
+
),
|
|
4717
|
+
),
|
|
4718
|
+
)
|
|
4709
4719
|
|
|
4710
4720
|
@action(
|
|
4711
4721
|
detail=True,
|
|
@@ -4716,28 +4726,7 @@ class ControllerUIViewSet(NautobotUIViewSet):
|
|
|
4716
4726
|
custom_view_additional_permissions=["wireless.view_controllermanageddevicegroupwirelessnetworkassignment"],
|
|
4717
4727
|
)
|
|
4718
4728
|
def wirelessnetworks(self, request, *args, **kwargs):
|
|
4719
|
-
|
|
4720
|
-
controller_managed_device_groups = instance.controller_managed_device_groups.restrict(
|
|
4721
|
-
request.user, "view"
|
|
4722
|
-
).values_list("pk", flat=True)
|
|
4723
|
-
wireless_networks = ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.filter(
|
|
4724
|
-
controller_managed_device_group__in=list(controller_managed_device_groups)
|
|
4725
|
-
).select_related("wireless_network")
|
|
4726
|
-
wireless_networks_table = ControllerManagedDeviceGroupWirelessNetworkAssignmentTable(
|
|
4727
|
-
data=wireless_networks, user=request.user, orderable=False
|
|
4728
|
-
)
|
|
4729
|
-
wireless_networks_table.columns.hide("controller")
|
|
4730
|
-
|
|
4731
|
-
RequestConfig(
|
|
4732
|
-
request, paginate={"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
4733
|
-
).configure(wireless_networks_table)
|
|
4734
|
-
|
|
4735
|
-
return Response(
|
|
4736
|
-
{
|
|
4737
|
-
"wireless_networks_table": wireless_networks_table,
|
|
4738
|
-
"active_tab": "wireless-networks",
|
|
4739
|
-
}
|
|
4740
|
-
)
|
|
4729
|
+
return Response({})
|
|
4741
4730
|
|
|
4742
4731
|
|
|
4743
4732
|
class ControllerManagedDeviceGroupUIViewSet(NautobotUIViewSet):
|
|
@@ -720,8 +720,8 @@ def refresh_job_code_from_repository(repository_slug, skip_reimport=False, ignor
|
|
|
720
720
|
After cloning/updating/deleting a GitRepository on disk, call this function to reload and reregister its Python.
|
|
721
721
|
|
|
722
722
|
Args:
|
|
723
|
-
repository_slug (str): Repository
|
|
724
|
-
skip_reimport (bool): If True, unload existing
|
|
723
|
+
repository_slug (str): Repository directory in GIT_ROOT that was updated or deleted.
|
|
724
|
+
skip_reimport (bool): If True, unload existing code from this repository but do not re-import it.
|
|
725
725
|
ignore_import_errors (bool): If True, any exceptions raised in the import will be caught and logged.
|
|
726
726
|
If False, exceptions will be re-raised after logging.
|
|
727
727
|
"""
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from django.template import Context
|
|
2
|
+
from django.template.loader import render_to_string
|
|
3
|
+
from django.utils.html import format_html, format_html_join
|
|
4
|
+
from django.utils.safestring import mark_safe
|
|
5
|
+
|
|
6
|
+
from nautobot.core.templatetags import helpers
|
|
7
|
+
from nautobot.core.ui.object_detail import Button, KeyValueTablePanel, ObjectFieldsPanel
|
|
8
|
+
from nautobot.core.views.utils import get_obj_from_context
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class JobRunScheduleButton(Button):
|
|
12
|
+
"""
|
|
13
|
+
A custom button for running or scheduling a job.
|
|
14
|
+
|
|
15
|
+
This button is rendered only if the user has the 'extras.run_job' permission.
|
|
16
|
+
It also disables itself (via HTML 'disabled' attribute) if the related object is not
|
|
17
|
+
installed or not enabled.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def get_extra_context(self, context):
|
|
21
|
+
"""Inject dynamic attributes (e.g. 'disabled') based on object state into the rendering context."""
|
|
22
|
+
extra_context = super().get_extra_context(context)
|
|
23
|
+
obj = context.get("object")
|
|
24
|
+
if not obj.installed or not obj.enabled:
|
|
25
|
+
if extra_context["attributes"] is None:
|
|
26
|
+
extra_context["attributes"] = {}
|
|
27
|
+
extra_context["attributes"]["disabled"] = "disabled"
|
|
28
|
+
return extra_context
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class JobKeyValueOverrideValueTablePanel(KeyValueTablePanel):
|
|
32
|
+
"""A table panel for displaying key-value pairs of job-related attributes, along with any override values defined on the job object."""
|
|
33
|
+
|
|
34
|
+
def _render_overridden_text(self, text):
|
|
35
|
+
"""
|
|
36
|
+
Render a simple overridden value inside a muted <span>, with a customizable prefix.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
value (str): The content to display.
|
|
40
|
+
prefix (str): The label shown before the value (default: 'default is').
|
|
41
|
+
"""
|
|
42
|
+
return format_html('<span class="text-muted">overridden; default is {}</span>', mark_safe(text)) # noqa: S308
|
|
43
|
+
|
|
44
|
+
def _render_overridden_block(self, content):
|
|
45
|
+
"""
|
|
46
|
+
Render a more complex block of HTML content (like rendered markdown or JSON)
|
|
47
|
+
in a div with a muted label indicating it's an overridden value.
|
|
48
|
+
"""
|
|
49
|
+
return format_html('<div class="text-muted">overridden; default is:<br>{}</div>', content)
|
|
50
|
+
|
|
51
|
+
def render_description_default(self, default_value):
|
|
52
|
+
"""
|
|
53
|
+
Render a description field's default value as markdown.
|
|
54
|
+
|
|
55
|
+
If the default value is a string, it is rendered as markdown;
|
|
56
|
+
otherwise, a placeholder is shown.
|
|
57
|
+
"""
|
|
58
|
+
rendered = (
|
|
59
|
+
helpers.render_markdown(default_value)
|
|
60
|
+
if isinstance(default_value, str)
|
|
61
|
+
else helpers.placeholder(default_value)
|
|
62
|
+
)
|
|
63
|
+
return self._render_overridden_block(rendered)
|
|
64
|
+
|
|
65
|
+
def render_time_limit_default(self, default_value, system_default_value, override=True):
|
|
66
|
+
"""
|
|
67
|
+
Render a time limit value, falling back to the system default if needed.
|
|
68
|
+
"""
|
|
69
|
+
message = "{} seconds" if default_value > 0 else "{} seconds (system default)"
|
|
70
|
+
value = default_value if default_value > 0 else system_default_value
|
|
71
|
+
if not override:
|
|
72
|
+
return message.format(value)
|
|
73
|
+
return self._render_overridden_text(message.format(value))
|
|
74
|
+
|
|
75
|
+
def render_job_queues_default(self, obj):
|
|
76
|
+
"""
|
|
77
|
+
Render the job's default task queues as JSON.
|
|
78
|
+
|
|
79
|
+
Attempts to retrieve the `task_queues` from the job's class and render
|
|
80
|
+
it using a JSON template. Falls back to a placeholder on error.
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
data = obj.job_class.task_queues
|
|
84
|
+
rendered = render_to_string("extras/inc/json_data.html", {"data": data, "format": "json"})
|
|
85
|
+
except Exception:
|
|
86
|
+
rendered = helpers.placeholder(None)
|
|
87
|
+
return self._render_overridden_block(rendered)
|
|
88
|
+
|
|
89
|
+
def render_default_job_queue_default(self, obj):
|
|
90
|
+
"""
|
|
91
|
+
Render the default job queue name from the job class.
|
|
92
|
+
|
|
93
|
+
Retrieves the first queue from `task_queues` if available.
|
|
94
|
+
Falls back to a placeholder if no queues are present.
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
queue = obj.job_class.task_queues[0]
|
|
98
|
+
except (AttributeError, IndexError, TypeError):
|
|
99
|
+
queue = helpers.placeholder("not specified")
|
|
100
|
+
return self._render_overridden_text(queue)
|
|
101
|
+
|
|
102
|
+
def render_boolean_default(self, default_value):
|
|
103
|
+
"""Render a boolean default value using a standardized visual format."""
|
|
104
|
+
return self._render_overridden_text(helpers.render_boolean(default_value))
|
|
105
|
+
|
|
106
|
+
def render_override_value(self, key, obj):
|
|
107
|
+
"""Render the override value for a given key on a job object."""
|
|
108
|
+
override_attr = f"{key}_override"
|
|
109
|
+
|
|
110
|
+
if not getattr(obj, override_attr, None):
|
|
111
|
+
return ""
|
|
112
|
+
|
|
113
|
+
if not obj.installed:
|
|
114
|
+
return self._render_overridden_text("default is unknown (not currently installed)")
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
default_value = getattr(obj.job_class, key)
|
|
118
|
+
except Exception:
|
|
119
|
+
default_value = "unknown"
|
|
120
|
+
|
|
121
|
+
renderers = {
|
|
122
|
+
"description": self.render_description_default,
|
|
123
|
+
"soft_time_limit": lambda v: self.render_time_limit_default(
|
|
124
|
+
v, helpers.settings_or_config("CELERY_TASK_SOFT_TIME_LIMIT")
|
|
125
|
+
),
|
|
126
|
+
"time_limit": lambda v: self.render_time_limit_default(
|
|
127
|
+
v, helpers.settings_or_config("CELERY_TASK_TIME_LIMIT")
|
|
128
|
+
),
|
|
129
|
+
"job_queues": lambda _: self.render_job_queues_default(obj),
|
|
130
|
+
"default_job_queue": lambda _: self.render_default_job_queue_default(obj),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if key in renderers:
|
|
134
|
+
return renderers[key](default_value)
|
|
135
|
+
|
|
136
|
+
if isinstance(default_value, bool):
|
|
137
|
+
return self.render_boolean_default(default_value)
|
|
138
|
+
|
|
139
|
+
return self._render_overridden_text(f'"{default_value}"')
|
|
140
|
+
|
|
141
|
+
def render_job_queues_list(self, value):
|
|
142
|
+
"""Renders a <ul> HTML list of job queues with hyperlinks, or a placeholder if none exist."""
|
|
143
|
+
if not value or not value.exists():
|
|
144
|
+
return helpers.placeholder(None)
|
|
145
|
+
|
|
146
|
+
items = format_html_join("\n", "<li>{}</li>", ((helpers.hyperlinked_object(q),) for q in value.all()))
|
|
147
|
+
return format_html("<ul>{}</ul>", items)
|
|
148
|
+
|
|
149
|
+
def render_body_content(self, context: Context):
|
|
150
|
+
"""Render the body content of the panel as a table of key-value rows, including any override information."""
|
|
151
|
+
data = self.get_data(context)
|
|
152
|
+
obj = get_obj_from_context(context)
|
|
153
|
+
|
|
154
|
+
if not data:
|
|
155
|
+
return format_html('<tr><td colspan="2">{}</td></tr>', helpers.placeholder(data))
|
|
156
|
+
|
|
157
|
+
result = format_html("")
|
|
158
|
+
panel_label = helpers.slugify(self.label or "")
|
|
159
|
+
for key, value in data.items():
|
|
160
|
+
key_display = self.render_key(key, value, context)
|
|
161
|
+
override_value_display = self.render_override_value(key, obj)
|
|
162
|
+
if value_display := self.render_value(key, value, context):
|
|
163
|
+
if value_display is helpers.HTML_NONE:
|
|
164
|
+
value_tag = value_display
|
|
165
|
+
else:
|
|
166
|
+
value_tag = format_html(
|
|
167
|
+
"""
|
|
168
|
+
<span class="hover_copy">
|
|
169
|
+
<span id="{unique_id}_value_{key}">{value}</span>
|
|
170
|
+
<button class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#{unique_id}_value_{key}">
|
|
171
|
+
<span class="mdi mdi-content-copy"></span>
|
|
172
|
+
</button>
|
|
173
|
+
</span>
|
|
174
|
+
""",
|
|
175
|
+
# key might not be globally unique in a page, but is unique to a panel;
|
|
176
|
+
# Hence we add the panel label to make it globally unique to the page
|
|
177
|
+
unique_id=panel_label,
|
|
178
|
+
key=helpers.slugify(key),
|
|
179
|
+
value=value_display,
|
|
180
|
+
)
|
|
181
|
+
result += format_html(
|
|
182
|
+
"<tr><td>{}</td><td>{}</td><td>{}</td></tr>",
|
|
183
|
+
key_display,
|
|
184
|
+
value_tag,
|
|
185
|
+
override_value_display,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return result
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class JobObjectFieldsPanel(ObjectFieldsPanel, JobKeyValueOverrideValueTablePanel):
|
|
192
|
+
"""
|
|
193
|
+
ObjectFieldsPanel that renders its fields in a 3-column layout.
|
|
194
|
+
Inherits behavior from ObjectFieldsPanel but overrides rendering with JobKeyValueOverrideValueTablePanel.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def render_value(self, key, value, context: Context):
|
|
198
|
+
if key == "job_queues":
|
|
199
|
+
return self.render_job_queues_list(value)
|
|
200
|
+
if key == "soft_time_limit":
|
|
201
|
+
value = self.render_time_limit_default(
|
|
202
|
+
value, helpers.settings_or_config("CELERY_TASK_SOFT_TIME_LIMIT"), override=False
|
|
203
|
+
)
|
|
204
|
+
if key == "time_limit":
|
|
205
|
+
value = self.render_time_limit_default(
|
|
206
|
+
value, helpers.settings_or_config("CELERY_TASK_TIME_LIMIT"), override=False
|
|
207
|
+
)
|
|
208
|
+
return super().render_value(key, value, context)
|