nautobot 2.4.11__py3-none-any.whl → 2.4.12__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/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/models/devices.py +10 -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/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/urls.py +1 -58
- nautobot/dcim/views.py +145 -189
- 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 +80 -27
- 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 +2 -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 +111 -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.12.dist-info}/METADATA +1 -1
- {nautobot-2.4.11.dist-info → nautobot-2.4.12.dist-info}/RECORD +354 -349
- {nautobot-2.4.11.dist-info → nautobot-2.4.12.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.11.dist-info → nautobot-2.4.12.dist-info}/NOTICE +0 -0
- {nautobot-2.4.11.dist-info → nautobot-2.4.12.dist-info}/WHEEL +0 -0
- {nautobot-2.4.11.dist-info → nautobot-2.4.12.dist-info}/entry_points.txt +0 -0
|
@@ -6,117 +6,4 @@
|
|
|
6
6
|
<li><a href="{% url 'circuits:circuit_list' %}?provider={{ object.circuit.provider.pk }}">{{ object.circuit.provider }}</a></li>
|
|
7
7
|
<li>{{ object.circuit|hyperlinked_object }}</li>
|
|
8
8
|
<li>{{ object|hyperlinked_object }}</li>
|
|
9
|
-
{% endblock breadcrumbs %}
|
|
10
|
-
|
|
11
|
-
{% block content_left_page %}
|
|
12
|
-
<div class="panel panel-default">
|
|
13
|
-
<table class="table table-hover panel-body attr-table">
|
|
14
|
-
<tr>
|
|
15
|
-
{% if object.location %}
|
|
16
|
-
<tr>
|
|
17
|
-
<td>Location</td>
|
|
18
|
-
<td>{% include 'dcim/inc/location_hierarchy.html' with location=object.location %}</td>
|
|
19
|
-
</tr>
|
|
20
|
-
{% endif %}
|
|
21
|
-
{% if object.provider_network %}
|
|
22
|
-
<td>Provider Network</td>
|
|
23
|
-
<td>
|
|
24
|
-
{{ object.provider_network|hyperlinked_object }}
|
|
25
|
-
</td>
|
|
26
|
-
{% endif %}
|
|
27
|
-
{% if object.cloud_network %}
|
|
28
|
-
<td>Cloud Network</td>
|
|
29
|
-
<td>
|
|
30
|
-
{{ object.cloud_network|hyperlinked_object }}
|
|
31
|
-
</td>
|
|
32
|
-
{% endif %}
|
|
33
|
-
</tr>
|
|
34
|
-
{% if object.location %}
|
|
35
|
-
<tr>
|
|
36
|
-
<td>Cable</td>
|
|
37
|
-
<td>
|
|
38
|
-
{% if object.cable %}
|
|
39
|
-
{% if perms.dcim.delete_cable %}
|
|
40
|
-
<div class="pull-right">
|
|
41
|
-
<a href="{% url 'dcim:cable_delete' pk=object.cable.pk %}?return_url={{ object.circuit.get_absolute_url }}" title="Remove cable" class="btn btn-danger btn-xs">
|
|
42
|
-
<i class="mdi mdi-ethernet-cable-off" aria-hidden="true"></i> Disconnect
|
|
43
|
-
</a>
|
|
44
|
-
</div>
|
|
45
|
-
{% endif %}
|
|
46
|
-
{{ object.cable|hyperlinked_object }}
|
|
47
|
-
<a href="{% url 'circuits:circuittermination_trace' pk=object.pk %}" class="btn btn-primary btn-xs" title="Trace">
|
|
48
|
-
<i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
|
|
49
|
-
</a>
|
|
50
|
-
{% with peer=object.get_cable_peer %}
|
|
51
|
-
to
|
|
52
|
-
{% if peer.device %}
|
|
53
|
-
{{ peer.device|hyperlinked_object }}
|
|
54
|
-
{% elif peer.circuit %}
|
|
55
|
-
{{ peer.circuit|hyperlinked_object }}
|
|
56
|
-
{% endif %}
|
|
57
|
-
({{ peer }})
|
|
58
|
-
{% endwith %}
|
|
59
|
-
{% else %}
|
|
60
|
-
{% if perms.dcim.add_cable %}
|
|
61
|
-
<div class="pull-right">
|
|
62
|
-
<span class="dropdown">
|
|
63
|
-
<button type="button" class="btn btn-success btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
64
|
-
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect
|
|
65
|
-
</button>
|
|
66
|
-
<ul class="dropdown-menu dropdown-menu-right">
|
|
67
|
-
<li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='interface' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Interface</a></li>
|
|
68
|
-
<li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='front-port' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Front Port</a></li>
|
|
69
|
-
<li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='rear-port' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Rear Port</a></li>
|
|
70
|
-
<li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='circuit-termination' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Circuit Termination</a></li>
|
|
71
|
-
</ul>
|
|
72
|
-
</span>
|
|
73
|
-
</div>
|
|
74
|
-
{% endif %}
|
|
75
|
-
<span class="text-muted">Not defined</span>
|
|
76
|
-
{% endif %}
|
|
77
|
-
</td>
|
|
78
|
-
</tr>
|
|
79
|
-
{% endif %}
|
|
80
|
-
<tr>
|
|
81
|
-
<td>Speed</td>
|
|
82
|
-
<td>
|
|
83
|
-
{% if object.port_speed and object.upstream_speed %}
|
|
84
|
-
<i class="mdi mdi-arrow-down-bold" title="Downstream"></i> {{ object.port_speed|humanize_speed }}
|
|
85
|
-
<i class="mdi mdi-arrow-up-bold" title="Upstream"></i> {{ object.upstream_speed|humanize_speed }}
|
|
86
|
-
{% elif object.port_speed %}
|
|
87
|
-
{{ object.port_speed|humanize_speed }}
|
|
88
|
-
{% else %}
|
|
89
|
-
<span class="text-muted">—</span>
|
|
90
|
-
{% endif %}
|
|
91
|
-
</td>
|
|
92
|
-
</tr>
|
|
93
|
-
<tr>
|
|
94
|
-
<td>IP Addressing</td>
|
|
95
|
-
<td>
|
|
96
|
-
{% if object.connected_endpoint %}
|
|
97
|
-
{% for ip in object.ip_addresses %}
|
|
98
|
-
{% if not forloop.first %}<br />{% endif %}
|
|
99
|
-
{{ ip|hyperlinked_object }} ({{ ip.vrf|default:"Global" }})
|
|
100
|
-
{% empty %}
|
|
101
|
-
<span class="text-muted">None</span>
|
|
102
|
-
{% endfor %}
|
|
103
|
-
{% else %}
|
|
104
|
-
<span class="text-muted">—</span>
|
|
105
|
-
{% endif %}
|
|
106
|
-
</td>
|
|
107
|
-
</tr>
|
|
108
|
-
<tr>
|
|
109
|
-
<td>Cross-Connect</td>
|
|
110
|
-
<td>{{ object.xconnect_id|placeholder }}</td>
|
|
111
|
-
</tr>
|
|
112
|
-
<tr>
|
|
113
|
-
<td>Patch Panel/Port</td>
|
|
114
|
-
<td>{{ object.pp_info|placeholder }}</td>
|
|
115
|
-
</tr>
|
|
116
|
-
<tr>
|
|
117
|
-
<td>Description</td>
|
|
118
|
-
<td>{{ object.description|placeholder }}</td>
|
|
119
|
-
</tr>
|
|
120
|
-
</table>
|
|
121
|
-
</div>
|
|
122
|
-
{% endblock content_left_page %}
|
|
9
|
+
{% endblock breadcrumbs %}
|
nautobot/circuits/views.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from django.contrib import messages
|
|
2
2
|
from django.db import transaction
|
|
3
3
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
4
|
-
from django.utils.html import format_html
|
|
4
|
+
from django.utils.html import format_html, format_html_join
|
|
5
5
|
|
|
6
6
|
from nautobot.core.forms import ConfirmationForm
|
|
7
|
-
from nautobot.core.templatetags
|
|
7
|
+
from nautobot.core.templatetags import helpers
|
|
8
8
|
from nautobot.core.ui.choices import SectionChoices
|
|
9
9
|
from nautobot.core.ui.object_detail import (
|
|
10
10
|
ObjectDetailContent,
|
|
@@ -13,6 +13,7 @@ from nautobot.core.ui.object_detail import (
|
|
|
13
13
|
)
|
|
14
14
|
from nautobot.core.ui.utils import render_component_template
|
|
15
15
|
from nautobot.core.views import generic
|
|
16
|
+
from nautobot.core.views.utils import get_obj_from_context
|
|
16
17
|
from nautobot.core.views.viewsets import NautobotUIViewSet
|
|
17
18
|
|
|
18
19
|
from . import filters, forms, tables
|
|
@@ -50,6 +51,38 @@ class CircuitTypeUIViewSet(NautobotUIViewSet):
|
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
class CircuitTerminationObjectFieldsPanel(ObjectFieldsPanel):
|
|
55
|
+
def get_extra_context(self, context):
|
|
56
|
+
return {"termination": context["object"]}
|
|
57
|
+
|
|
58
|
+
def render_key(self, key, value, context):
|
|
59
|
+
if key == "connected_endpoint":
|
|
60
|
+
return "IP Addressing"
|
|
61
|
+
return super().render_key(key, value, context)
|
|
62
|
+
|
|
63
|
+
def render_value(self, key, value, context):
|
|
64
|
+
instance = get_obj_from_context(context, self.context_object_key)
|
|
65
|
+
location = getattr(instance, "location", None)
|
|
66
|
+
|
|
67
|
+
# Cable column is hidden if the location is unset
|
|
68
|
+
if not location and key == "cable":
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
if location and key == "cable":
|
|
72
|
+
return render_component_template("circuits/inc/circuit_termination_cable_fragment.html", context)
|
|
73
|
+
|
|
74
|
+
if key == "connected_endpoint":
|
|
75
|
+
ip_addresses = getattr(value, "ip_addresses", None)
|
|
76
|
+
if not ip_addresses or not ip_addresses.exists():
|
|
77
|
+
return helpers.HTML_NONE
|
|
78
|
+
return format_html_join(
|
|
79
|
+
", ",
|
|
80
|
+
"{} ({})",
|
|
81
|
+
((helpers.hyperlinked_object(ip), getattr(ip, "vrf", None) or "Global") for ip in ip_addresses.all()),
|
|
82
|
+
)
|
|
83
|
+
return super().render_value(key, value, context)
|
|
84
|
+
|
|
85
|
+
|
|
53
86
|
class CircuitTerminationUIViewSet(NautobotUIViewSet):
|
|
54
87
|
action_buttons = ("import", "export")
|
|
55
88
|
bulk_update_form_class = forms.CircuitTerminationBulkEditForm
|
|
@@ -60,6 +93,41 @@ class CircuitTerminationUIViewSet(NautobotUIViewSet):
|
|
|
60
93
|
serializer_class = serializers.CircuitTerminationSerializer
|
|
61
94
|
table_class = tables.CircuitTerminationTable
|
|
62
95
|
|
|
96
|
+
object_detail_content = ObjectDetailContent(
|
|
97
|
+
panels=(
|
|
98
|
+
CircuitTerminationObjectFieldsPanel(
|
|
99
|
+
section=SectionChoices.LEFT_HALF,
|
|
100
|
+
weight=100,
|
|
101
|
+
fields=[
|
|
102
|
+
"location",
|
|
103
|
+
"provider_network",
|
|
104
|
+
"cloud_network",
|
|
105
|
+
"cable",
|
|
106
|
+
"port_speed",
|
|
107
|
+
"upstream_speed",
|
|
108
|
+
"connected_endpoint",
|
|
109
|
+
"xconnect_id",
|
|
110
|
+
"pp_info",
|
|
111
|
+
"description",
|
|
112
|
+
],
|
|
113
|
+
hide_if_unset=[
|
|
114
|
+
"location",
|
|
115
|
+
"provider_network",
|
|
116
|
+
"cloud_network",
|
|
117
|
+
"port_speed",
|
|
118
|
+
"upstream_speed",
|
|
119
|
+
],
|
|
120
|
+
exclude_fields=[
|
|
121
|
+
"circuit",
|
|
122
|
+
],
|
|
123
|
+
value_transforms={
|
|
124
|
+
"port_speed": [helpers.humanize_speed],
|
|
125
|
+
"upstream_speed": [helpers.humanize_speed],
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
63
131
|
def get_object(self):
|
|
64
132
|
obj = super().get_object()
|
|
65
133
|
if self.action in ["create", "update"] and "circuit" in self.kwargs:
|
|
@@ -127,8 +195,8 @@ class CircuitUIViewSet(NautobotUIViewSet):
|
|
|
127
195
|
"description",
|
|
128
196
|
),
|
|
129
197
|
value_transforms={
|
|
130
|
-
"port_speed": [humanize_speed, placeholder],
|
|
131
|
-
"upstream_speed": [humanize_speed],
|
|
198
|
+
"port_speed": [helpers.humanize_speed, helpers.placeholder],
|
|
199
|
+
"upstream_speed": [helpers.humanize_speed],
|
|
132
200
|
},
|
|
133
201
|
hide_if_unset=("location", "provider_network", "cloud_network", "upstream_speed"),
|
|
134
202
|
ignore_nonexistent_fields=True, # ip_addresses may be undefined
|
|
@@ -173,8 +241,10 @@ class CircuitUIViewSet(NautobotUIViewSet):
|
|
|
173
241
|
'<span title="{} ({})">{}</span>',
|
|
174
242
|
relationship.label,
|
|
175
243
|
relationship.key,
|
|
176
|
-
bettertitle(relationship.get_label(side)),
|
|
244
|
+
helpers.bettertitle(relationship.get_label(side)),
|
|
177
245
|
)
|
|
246
|
+
if key == "ip_addresses":
|
|
247
|
+
return "IP Addressing"
|
|
178
248
|
return super().render_key(key, value, context)
|
|
179
249
|
|
|
180
250
|
def render_value(self, key, value, context):
|
|
@@ -209,7 +279,7 @@ class CircuitUIViewSet(NautobotUIViewSet):
|
|
|
209
279
|
weight=100,
|
|
210
280
|
fields="__all__",
|
|
211
281
|
exclude_fields=["comments", "circuit_termination_a", "circuit_termination_z"],
|
|
212
|
-
value_transforms={"commit_rate": [humanize_speed, placeholder]},
|
|
282
|
+
value_transforms={"commit_rate": [helpers.humanize_speed, helpers.placeholder]},
|
|
213
283
|
),
|
|
214
284
|
CircuitTerminationPanel(
|
|
215
285
|
label="Termination - A Side",
|
nautobot/cloud/navigation.py
CHANGED
nautobot/core/jobs/cleanup.py
CHANGED
|
@@ -65,18 +65,18 @@ class LogsCleanup(Job):
|
|
|
65
65
|
related_model = related_object.related_model
|
|
66
66
|
related_field_name = related_object.field.name
|
|
67
67
|
cascade_queryset = related_model.objects.filter(**{f"{related_field_name}__id__in": queryset})
|
|
68
|
-
|
|
68
|
+
if cascade_queryset.exists():
|
|
69
|
+
self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
|
|
69
70
|
|
|
70
71
|
genericrelation_related_fields = [
|
|
71
72
|
field for field in queryset.model._meta.private_fields if hasattr(field, "bulk_related_objects")
|
|
72
73
|
]
|
|
73
74
|
for gr_related_field in genericrelation_related_fields:
|
|
74
75
|
related_model = gr_related_field.related_model
|
|
75
|
-
if related_model == queryset.model:
|
|
76
|
-
continue # avoid infinite recursion # TODO: this won't catch A<-B<-A<-B loops...
|
|
77
76
|
related_field_name = gr_related_field.related_query_name()
|
|
78
77
|
cascade_queryset = related_model.objects.filter(**{f"{related_field_name}__id__in": queryset})
|
|
79
|
-
|
|
78
|
+
if cascade_queryset.exists():
|
|
79
|
+
self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
|
|
80
80
|
|
|
81
81
|
deleted_count = queryset._raw_delete(using="default")
|
|
82
82
|
if deleted_count:
|
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
{% load helpers %}
|
|
2
2
|
|
|
3
|
-
<strong>{{ body_content_table_verbose_name_plural|bettertitle }}</strong>
|
|
3
|
+
<strong>{{ body_content_table_verbose_name_plural|bettertitle }}</strong>
|
|
4
|
+
{% if body_content_table_list_url %}
|
|
5
|
+
<a href="{{ body_content_table_list_url }}" class="badge badge-primary">{% if badge_count_override %}{{ badge_count_override }}{% else %}{{ body_content_table.rows|length }}{% endif %}</a>
|
|
6
|
+
{% endif %}
|
|
7
|
+
|
|
8
|
+
<div class="pull-right noprint">
|
|
9
|
+
{% if request.user.is_authenticated %}
|
|
10
|
+
{% table_config_button_small body_content_table %}
|
|
11
|
+
{% endif %}
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
{% table_config_form body_content_table %}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import os.path
|
|
3
|
-
import sys
|
|
4
|
-
import tempfile
|
|
5
1
|
from unittest import mock
|
|
6
2
|
import uuid
|
|
7
3
|
|
|
@@ -21,11 +17,7 @@ from nautobot.core.models import fields as core_fields, utils as models_utils, v
|
|
|
21
17
|
from nautobot.core.testing import TestCase
|
|
22
18
|
from nautobot.core.utils import data as data_utils, filtering, lookup, querysets, requests
|
|
23
19
|
from nautobot.core.utils.migrations import update_object_change_ct_for_replaced_models
|
|
24
|
-
from nautobot.core.utils.module_loading import
|
|
25
|
-
check_name_safe_to_import_privately,
|
|
26
|
-
clear_module_from_sys_modules,
|
|
27
|
-
import_modules_privately,
|
|
28
|
-
)
|
|
20
|
+
from nautobot.core.utils.module_loading import check_name_safe_to_import_privately
|
|
29
21
|
from nautobot.dcim import filters as dcim_filters, forms as dcim_forms, models as dcim_models, tables
|
|
30
22
|
from nautobot.extras import models as extras_models, utils as extras_utils
|
|
31
23
|
from nautobot.extras.choices import ObjectChangeActionChoices, RelationshipTypeChoices
|
|
@@ -983,174 +975,6 @@ class TestModuleLoadingUtils(TestCase):
|
|
|
983
975
|
self.assertFalse(permitted)
|
|
984
976
|
self.assertIsInstance(reason, str)
|
|
985
977
|
|
|
986
|
-
def _create_test_files(self, root_directory: str, contents: dict):
|
|
987
|
-
"""Helper function to create arbitrary text files in a given directory."""
|
|
988
|
-
for relative_path, file_contents in contents.items():
|
|
989
|
-
os.makedirs(os.path.dirname(os.path.join(root_directory, relative_path)), exist_ok=True)
|
|
990
|
-
with open(os.path.join(root_directory, relative_path), "wt") as fd:
|
|
991
|
-
fd.write(file_contents)
|
|
992
|
-
|
|
993
|
-
def test_import_modules_privately_jobs_root_case(self):
|
|
994
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
995
|
-
try:
|
|
996
|
-
contents = {
|
|
997
|
-
# Job file treated as a standalone module
|
|
998
|
-
"some_jobs.py": 'name = "some_jobs"',
|
|
999
|
-
# Job subdirectory treated as a package
|
|
1000
|
-
"my_jobs/__init__.py": '''\
|
|
1001
|
-
import my_jobs.some_submodule
|
|
1002
|
-
from . import relative_submodule
|
|
1003
|
-
name = "my_jobs"''',
|
|
1004
|
-
"my_jobs/some_submodule/__init__.py": 'name = "my_jobs.some_submodule"',
|
|
1005
|
-
"my_jobs/relative_submodule/__init__.py": 'name = "my_jobs.relative_submodule"',
|
|
1006
|
-
# Job file that shouldn't be loaded as it conflicts
|
|
1007
|
-
"tkinter.py": 'name = "tkinter"',
|
|
1008
|
-
# Job submodule that shouldn't be loaded as it conflicts
|
|
1009
|
-
"turtle/__init__.py": 'name = "turtle"',
|
|
1010
|
-
}
|
|
1011
|
-
self._create_test_files(tempdir, contents)
|
|
1012
|
-
|
|
1013
|
-
modules = import_modules_privately(tempdir, ignore_import_errors=False)
|
|
1014
|
-
self.assertEqual(["my_jobs", "some_jobs"], sorted([module.__name__ for module in modules]))
|
|
1015
|
-
# assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
|
|
1016
|
-
if "some_jobs" not in sys.modules:
|
|
1017
|
-
self.fail("Valid module wasn't loaded from JOBS_ROOT")
|
|
1018
|
-
if "my_jobs" not in sys.modules:
|
|
1019
|
-
self.fail("Valid package wasn't loaded from JOBS_ROOT")
|
|
1020
|
-
with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from JOBS_ROOT"):
|
|
1021
|
-
sys.modules["tkinter"]
|
|
1022
|
-
with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from JOBS_ROOT"):
|
|
1023
|
-
sys.modules["turtle"]
|
|
1024
|
-
|
|
1025
|
-
self.assertEqual(sys.modules["some_jobs"].name, "some_jobs")
|
|
1026
|
-
self.assertEqual(sys.modules["my_jobs"].name, "my_jobs")
|
|
1027
|
-
self.assertEqual(sys.modules["my_jobs"].some_submodule.name, "my_jobs.some_submodule")
|
|
1028
|
-
# self.assertEqual(sys.modules["my_jobs"].relative_submodule.name, "my_jobs.relative_submodule")
|
|
1029
|
-
|
|
1030
|
-
finally:
|
|
1031
|
-
clear_module_from_sys_modules("some_jobs")
|
|
1032
|
-
clear_module_from_sys_modules("my_jobs")
|
|
1033
|
-
|
|
1034
|
-
self.assertNotIn("some_jobs", sys.modules.keys())
|
|
1035
|
-
self.assertNotIn("my_jobs", sys.modules.keys())
|
|
1036
|
-
self.assertNotIn("my_jobs.some_submodule", sys.modules.keys())
|
|
1037
|
-
|
|
1038
|
-
# Test reloading of modules after code changes
|
|
1039
|
-
try:
|
|
1040
|
-
contents["some_jobs.py"] = 'name = "some_jobs_new"'
|
|
1041
|
-
contents["my_jobs/__init__.py"] = '''\
|
|
1042
|
-
import my_jobs.some_submodule
|
|
1043
|
-
from . import relative_submodule
|
|
1044
|
-
name = "my_jobs_new"'''
|
|
1045
|
-
contents["my_jobs/some_submodule/__init__.py"] = 'name = "my_jobs.some_submodule_new"'
|
|
1046
|
-
self._create_test_files(tempdir, contents)
|
|
1047
|
-
|
|
1048
|
-
modules = import_modules_privately(tempdir, ignore_import_errors=False)
|
|
1049
|
-
self.assertEqual(["my_jobs", "some_jobs"], sorted([module.__name__ for module in modules]))
|
|
1050
|
-
# assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
|
|
1051
|
-
if "some_jobs" not in sys.modules:
|
|
1052
|
-
self.fail("Valid module wasn't loaded from JOBS_ROOT")
|
|
1053
|
-
if "my_jobs" not in sys.modules:
|
|
1054
|
-
self.fail("Valid package wasn't loaded from JOBS_ROOT")
|
|
1055
|
-
with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from JOBS_ROOT"):
|
|
1056
|
-
sys.modules["tkinter"]
|
|
1057
|
-
with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from JOBS_ROOT"):
|
|
1058
|
-
sys.modules["turtle"]
|
|
1059
|
-
|
|
1060
|
-
self.assertEqual(sys.modules["some_jobs"].name, "some_jobs_new")
|
|
1061
|
-
self.assertEqual(sys.modules["my_jobs"].name, "my_jobs_new")
|
|
1062
|
-
self.assertEqual(sys.modules["my_jobs"].some_submodule.name, "my_jobs.some_submodule_new")
|
|
1063
|
-
|
|
1064
|
-
finally:
|
|
1065
|
-
clear_module_from_sys_modules("some_jobs")
|
|
1066
|
-
clear_module_from_sys_modules("my_jobs")
|
|
1067
|
-
|
|
1068
|
-
self.assertNotIn("some_jobs", sys.modules.keys())
|
|
1069
|
-
self.assertNotIn("my_jobs", sys.modules.keys())
|
|
1070
|
-
self.assertNotIn("my_jobs.some_submodule", sys.modules.keys())
|
|
1071
|
-
|
|
1072
|
-
def test_import_modules_privately_git_repo_jobs_case(self):
|
|
1073
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
1074
|
-
try:
|
|
1075
|
-
contents = {
|
|
1076
|
-
# Repo that we intend to load
|
|
1077
|
-
"my_repo/__init__.py": 'name = "my_repo"',
|
|
1078
|
-
"my_repo/jobs/__init__.py": '''\
|
|
1079
|
-
import my_repo.jobs.some_jobs
|
|
1080
|
-
from . import some_other_jobs
|
|
1081
|
-
name = "my_repo.jobs"''',
|
|
1082
|
-
"my_repo/jobs/some_jobs.py": 'name = "my_repo.jobs.some_jobs"',
|
|
1083
|
-
"my_repo/jobs/some_other_jobs.py": 'name = "my_repo.jobs.some_other_jobs"',
|
|
1084
|
-
# A separate repo, not intended to be loaded
|
|
1085
|
-
"other_repo/__init__.py": "",
|
|
1086
|
-
# File that shouldn't be loaded as it conflicts
|
|
1087
|
-
"tkinter.py": "",
|
|
1088
|
-
# Package that shouldn't be loaded as it conflicts
|
|
1089
|
-
"turtle/__init__.py": "",
|
|
1090
|
-
}
|
|
1091
|
-
self._create_test_files(tempdir, contents)
|
|
1092
|
-
|
|
1093
|
-
modules = import_modules_privately(tempdir, module_path=["my_repo", "jobs"], ignore_import_errors=False)
|
|
1094
|
-
self.assertEqual(["my_repo", "my_repo.jobs"], sorted([module.__name__ for module in modules]))
|
|
1095
|
-
# assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
|
|
1096
|
-
if "my_repo" not in sys.modules:
|
|
1097
|
-
self.fail("Valid repo wasn't loaded from GIT_ROOT")
|
|
1098
|
-
if "my_repo.jobs" not in sys.modules:
|
|
1099
|
-
self.fail("Valid repo subdirectory wasn't loaded from GIT_ROOT")
|
|
1100
|
-
with self.assertRaises(KeyError, msg="unexpected repo was loaded from GIT_ROOT"):
|
|
1101
|
-
sys.modules["other_repo"]
|
|
1102
|
-
with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from GIT_ROOT"):
|
|
1103
|
-
sys.modules["tkinter"]
|
|
1104
|
-
with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from GIT_ROOT"):
|
|
1105
|
-
sys.modules["turtle"]
|
|
1106
|
-
|
|
1107
|
-
self.assertEqual(sys.modules["my_repo"].name, "my_repo")
|
|
1108
|
-
self.assertEqual(sys.modules["my_repo.jobs"].name, "my_repo.jobs")
|
|
1109
|
-
self.assertEqual(sys.modules["my_repo.jobs"].some_jobs.name, "my_repo.jobs.some_jobs")
|
|
1110
|
-
self.assertEqual(sys.modules["my_repo.jobs"].some_other_jobs.name, "my_repo.jobs.some_other_jobs")
|
|
1111
|
-
|
|
1112
|
-
finally:
|
|
1113
|
-
clear_module_from_sys_modules("my_repo")
|
|
1114
|
-
|
|
1115
|
-
self.assertNotIn("my_repo", sys.modules.keys())
|
|
1116
|
-
self.assertNotIn("my_repo.jobs", sys.modules.keys())
|
|
1117
|
-
|
|
1118
|
-
# Test reloading of modules after code changes
|
|
1119
|
-
try:
|
|
1120
|
-
contents["my_repo/__init__.py"] = 'name = "my_repo_new"'
|
|
1121
|
-
contents["my_repo/jobs/__init__.py"] = '''\
|
|
1122
|
-
import my_repo.jobs.some_jobs
|
|
1123
|
-
from . import some_other_jobs
|
|
1124
|
-
name = "my_repo.jobs_new"'''
|
|
1125
|
-
contents["my_repo/jobs/some_jobs.py"] = 'name = "my_repo.jobs.some_jobs_new"'
|
|
1126
|
-
contents["my_repo/jobs/some_other_jobs.py"] = 'name = "my_repo.jobs.some_other_jobs_new"'
|
|
1127
|
-
self._create_test_files(tempdir, contents)
|
|
1128
|
-
|
|
1129
|
-
modules = import_modules_privately(tempdir, module_path=["my_repo", "jobs"], ignore_import_errors=False)
|
|
1130
|
-
self.assertEqual(["my_repo", "my_repo.jobs"], sorted([module.__name__ for module in modules]))
|
|
1131
|
-
# assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
|
|
1132
|
-
if "my_repo" not in sys.modules:
|
|
1133
|
-
self.fail("Valid repo wasn't loaded from GIT_ROOT")
|
|
1134
|
-
if "my_repo.jobs" not in sys.modules:
|
|
1135
|
-
self.fail("Valid repo subdirectory wasn't loaded from GIT_ROOT")
|
|
1136
|
-
with self.assertRaises(KeyError, msg="unexpected repo was loaded from GIT_ROOT"):
|
|
1137
|
-
sys.modules["other_repo"]
|
|
1138
|
-
with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from GIT_ROOT"):
|
|
1139
|
-
sys.modules["tkinter"]
|
|
1140
|
-
with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from GIT_ROOT"):
|
|
1141
|
-
sys.modules["turtle"]
|
|
1142
|
-
|
|
1143
|
-
self.assertEqual(sys.modules["my_repo"].name, "my_repo_new")
|
|
1144
|
-
self.assertEqual(sys.modules["my_repo.jobs"].name, "my_repo.jobs_new")
|
|
1145
|
-
self.assertEqual(sys.modules["my_repo.jobs"].some_jobs.name, "my_repo.jobs.some_jobs_new")
|
|
1146
|
-
self.assertEqual(sys.modules["my_repo.jobs"].some_other_jobs.name, "my_repo.jobs.some_other_jobs_new")
|
|
1147
|
-
|
|
1148
|
-
finally:
|
|
1149
|
-
clear_module_from_sys_modules("my_repo")
|
|
1150
|
-
|
|
1151
|
-
self.assertNotIn("my_repo", sys.modules.keys())
|
|
1152
|
-
self.assertNotIn("my_repo.jobs", sys.modules.keys())
|
|
1153
|
-
|
|
1154
978
|
|
|
1155
979
|
class TestQuerySetUtils(TestCase):
|
|
1156
980
|
def test_maybe_select_related(self):
|
|
@@ -764,11 +764,11 @@ class TestObjectDetailView(TestCase):
|
|
|
764
764
|
url = reverse("circuits:provider", args=(provider.pk,))
|
|
765
765
|
response = self.client.get(f"{url}?tab=main")
|
|
766
766
|
self.assertHttpStatus(response, 200)
|
|
767
|
-
response_data = response.content.decode(response.charset)
|
|
767
|
+
response_data = extract_page_body(response.content.decode(response.charset))
|
|
768
768
|
view_move_url = reverse("circuits:circuit_list") + f"?provider={provider.id}"
|
|
769
769
|
|
|
770
770
|
# Assert Badge Count in table panel header
|
|
771
|
-
panel_header = f"""<
|
|
771
|
+
panel_header = f"""<strong>Circuits</strong> <a href="{view_move_url}" class="badge badge-primary">10</a>"""
|
|
772
772
|
self.assertInHTML(panel_header, response_data)
|
|
773
773
|
|
|
774
774
|
# Assert view X more btn
|
|
@@ -867,7 +867,7 @@ class ObjectsTablePanel(Panel):
|
|
|
867
867
|
body_content_table_queryset = body_content_table_queryset.order_by(*self.order_by_fields)
|
|
868
868
|
body_content_table_queryset = body_content_table_queryset.distinct()
|
|
869
869
|
body_content_table = body_content_table_class(
|
|
870
|
-
body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui
|
|
870
|
+
body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui, user=request.user
|
|
871
871
|
)
|
|
872
872
|
if self.tab_id and "actions" in body_content_table.columns:
|
|
873
873
|
# Use the `self.tab_id`, if it exists, to determine the correct return URL for the table
|