nautobot 2.4.10__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/cloud/tests/test_views.py +13 -1
- nautobot/cloud/views.py +39 -9
- nautobot/core/celery/__init__.py +21 -0
- nautobot/core/celery/encoders.py +3 -0
- nautobot/core/forms/forms.py +4 -1
- nautobot/core/jobs/bulk_actions.py +8 -8
- nautobot/core/jobs/cleanup.py +11 -0
- nautobot/core/management/commands/generate_test_data.py +2 -1
- 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 +2 -1
- nautobot/core/testing/mixins.py +19 -1
- nautobot/core/testing/views.py +104 -8
- nautobot/core/tests/test_jobs.py +20 -4
- nautobot/core/tests/test_utils.py +17 -0
- nautobot/core/tests/test_views.py +2 -2
- nautobot/core/tests/test_views_utils.py +53 -2
- nautobot/core/ui/object_detail.py +5 -1
- nautobot/core/utils/lookup.py +4 -2
- nautobot/core/utils/module_loading.py +23 -0
- nautobot/core/views/generic.py +2 -12
- nautobot/core/views/mixins.py +19 -1
- nautobot/core/views/renderers.py +4 -13
- nautobot/core/views/utils.py +16 -0
- nautobot/dcim/api/serializers.py +13 -0
- nautobot/dcim/api/urls.py +1 -0
- nautobot/dcim/api/views.py +20 -0
- nautobot/dcim/apps.py +1 -0
- nautobot/dcim/factory.py +11 -0
- nautobot/dcim/filters/__init__.py +116 -0
- nautobot/dcim/filters/mixins.py +2 -1
- nautobot/dcim/forms.py +205 -19
- nautobot/dcim/migrations/0070_modulefamily_models.py +92 -0
- nautobot/dcim/models/__init__.py +2 -0
- nautobot/dcim/models/device_component_templates.py +14 -0
- nautobot/dcim/models/device_components.py +13 -1
- nautobot/dcim/models/devices.py +72 -0
- nautobot/dcim/navigation.py +16 -0
- nautobot/dcim/tables/__init__.py +2 -0
- nautobot/dcim/tables/devices.py +50 -0
- nautobot/dcim/tables/devicetypes.py +35 -1
- nautobot/dcim/tables/template_code.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/controllermanageddevicegroup_retrieve.html +1 -90
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +1 -63
- nautobot/dcim/templates/dcim/location.html +2 -249
- nautobot/dcim/templates/dcim/location_edit.html +2 -38
- nautobot/dcim/templates/dcim/location_retrieve.html +249 -0
- nautobot/dcim/templates/dcim/location_update.html +38 -0
- nautobot/dcim/templates/dcim/module_update.html +1 -0
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +93 -1
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +31 -0
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +6 -0
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -160
- 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_api.py +35 -0
- nautobot/dcim/tests/test_filters.py +102 -3
- nautobot/dcim/tests/test_models.py +146 -0
- nautobot/dcim/tests/test_views.py +70 -97
- nautobot/dcim/urls.py +5 -80
- nautobot/dcim/views.py +584 -342
- nautobot/extras/api/views.py +9 -2
- nautobot/extras/datasources/git.py +9 -1
- nautobot/extras/forms/forms.py +9 -5
- nautobot/extras/jobs.py +4 -2
- nautobot/extras/jobs_ui.py +208 -0
- nautobot/extras/models/datasources.py +5 -8
- nautobot/extras/models/jobs.py +5 -0
- nautobot/extras/models/tags.py +4 -0
- nautobot/extras/plugins/__init__.py +3 -0
- nautobot/extras/tables.py +40 -3
- nautobot/extras/templates/extras/configcontext.html +2 -220
- nautobot/extras/templates/extras/configcontext_edit.html +2 -50
- nautobot/extras/templates/extras/configcontext_retrieve.html +2 -0
- nautobot/extras/templates/extras/configcontext_update.html +50 -0
- nautobot/extras/templates/extras/configcontextschema.html +2 -48
- nautobot/extras/templates/extras/configcontextschema_edit.html +2 -19
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +48 -0
- nautobot/extras/templates/extras/configcontextschema_update.html +19 -0
- nautobot/extras/templates/extras/inc/configcontext_data.html +1 -0
- nautobot/extras/templates/extras/inc/configcontext_format.html +6 -0
- nautobot/extras/templates/extras/job_detail.html +1 -326
- nautobot/extras/templates/extras/job_edit.html +12 -6
- nautobot/extras/templates/extras/tag.html +2 -52
- nautobot/extras/templates/extras/tag_edit.html +2 -15
- nautobot/extras/templates/extras/tag_retrieve.html +2 -0
- nautobot/extras/templates/extras/tag_update.html +15 -0
- nautobot/extras/templates/extras/team_retrieve.html +2 -2
- nautobot/extras/tests/test_api.py +15 -15
- nautobot/extras/tests/test_filters.py +4 -4
- nautobot/extras/tests/test_jobs.py +23 -10
- nautobot/extras/tests/test_models.py +19 -8
- nautobot/extras/tests/test_plugins.py +6 -3
- nautobot/extras/tests/test_views.py +66 -11
- nautobot/extras/urls.py +4 -134
- nautobot/extras/views.py +186 -178
- nautobot/ipam/models.py +19 -4
- nautobot/ipam/tables.py +19 -0
- nautobot/ipam/templates/ipam/vlan.html +2 -84
- nautobot/ipam/templates/ipam/vlan_edit.html +2 -24
- nautobot/ipam/templates/ipam/vlan_retrieve.html +84 -0
- nautobot/ipam/templates/ipam/vlan_update.html +24 -0
- nautobot/ipam/tests/test_views.py +5 -0
- nautobot/ipam/urls.py +1 -21
- nautobot/ipam/views.py +45 -70
- nautobot/project-static/docs/404.html +33 -10
- nautobot/project-static/docs/apps/index.html +33 -10
- nautobot/project-static/docs/apps/nautobot-apps.html +33 -10
- 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 +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +122 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +33 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +33 -10
- nautobot/project-static/docs/development/apps/api/configuration-view.html +33 -10
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +33 -10
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +33 -10
- nautobot/project-static/docs/development/apps/api/models/global-search.html +33 -10
- nautobot/project-static/docs/development/apps/api/models/graphql.html +33 -10
- nautobot/project-static/docs/development/apps/api/models/index.html +33 -10
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +42 -10
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +34 -11
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +33 -10
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +33 -10
- nautobot/project-static/docs/development/apps/api/prometheus.html +33 -10
- nautobot/project-static/docs/development/apps/api/setup.html +33 -10
- nautobot/project-static/docs/development/apps/api/testing.html +33 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +33 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +33 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +33 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +33 -10
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/base-template.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/index.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/notes.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +33 -10
- nautobot/project-static/docs/development/apps/api/views/urls.html +33 -10
- nautobot/project-static/docs/development/apps/index.html +33 -10
- nautobot/project-static/docs/development/apps/migration/code-updates.html +33 -10
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +33 -10
- nautobot/project-static/docs/development/apps/migration/from-v1.html +33 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +33 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +33 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +33 -10
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +33 -10
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +33 -10
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +33 -10
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +33 -10
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +33 -10
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +33 -10
- nautobot/project-static/docs/development/core/application-registry.html +33 -10
- nautobot/project-static/docs/development/core/best-practices.html +33 -10
- nautobot/project-static/docs/development/core/bootstrap-ui.html +33 -10
- nautobot/project-static/docs/development/core/caching.html +33 -10
- nautobot/project-static/docs/development/core/controllers.html +33 -10
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +33 -10
- nautobot/project-static/docs/development/core/generic-views.html +33 -10
- nautobot/project-static/docs/development/core/getting-started.html +33 -10
- nautobot/project-static/docs/development/core/homepage.html +33 -10
- nautobot/project-static/docs/development/core/index.html +33 -10
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +33 -10
- nautobot/project-static/docs/development/core/model-checklist.html +33 -10
- nautobot/project-static/docs/development/core/model-features.html +33 -10
- nautobot/project-static/docs/development/core/natural-keys.html +33 -10
- nautobot/project-static/docs/development/core/navigation-menu.html +33 -10
- nautobot/project-static/docs/development/core/release-checklist.html +33 -10
- nautobot/project-static/docs/development/core/role-internals.html +33 -10
- nautobot/project-static/docs/development/core/settings.html +33 -10
- nautobot/project-static/docs/development/core/style-guide.html +33 -10
- nautobot/project-static/docs/development/core/templates.html +33 -10
- nautobot/project-static/docs/development/core/testing.html +33 -10
- nautobot/project-static/docs/development/core/ui-component-framework.html +33 -10
- nautobot/project-static/docs/development/core/user-preferences.html +33 -10
- nautobot/project-static/docs/development/index.html +33 -10
- nautobot/project-static/docs/development/jobs/getting-started.html +33 -10
- nautobot/project-static/docs/development/jobs/index.html +33 -10
- nautobot/project-static/docs/development/jobs/installation.html +33 -10
- nautobot/project-static/docs/development/jobs/job-extensions.html +33 -10
- nautobot/project-static/docs/development/jobs/job-logging.html +33 -10
- nautobot/project-static/docs/development/jobs/job-patterns.html +33 -10
- nautobot/project-static/docs/development/jobs/job-structure.html +33 -10
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +33 -10
- nautobot/project-static/docs/development/jobs/testing.html +33 -10
- nautobot/project-static/docs/index.html +33 -10
- nautobot/project-static/docs/insert-analytics.sh +36 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +33 -10
- nautobot/project-static/docs/overview/design_philosophy.html +33 -10
- nautobot/project-static/docs/release-notes/index.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.0.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.1.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.2.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.3.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.4.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.5.html +33 -10
- nautobot/project-static/docs/release-notes/version-1.6.html +33 -10
- nautobot/project-static/docs/release-notes/version-2.0.html +33 -10
- nautobot/project-static/docs/release-notes/version-2.1.html +33 -10
- nautobot/project-static/docs/release-notes/version-2.2.html +33 -10
- nautobot/project-static/docs/release-notes/version-2.3.html +33 -10
- nautobot/project-static/docs/release-notes/version-2.4.html +363 -10
- nautobot/project-static/docs/requirements.txt +1 -1
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +302 -298
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +33 -10
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +33 -10
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +33 -10
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +33 -10
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +33 -10
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +33 -10
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +33 -10
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/index.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +33 -10
- nautobot/project-static/docs/user-guide/administration/installation/services.html +33 -10
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +33 -10
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +33 -10
- nautobot/project-static/docs/user-guide/administration/security/index.html +33 -10
- nautobot/project-static/docs/user-guide/administration/security/notices.html +33 -10
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +33 -10
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +33 -10
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +45 -22
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +37 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +37 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +37 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +10261 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +36 -13
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +33 -10
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +44 -18
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +33 -10
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +33 -10
- nautobot/project-static/docs/user-guide/index.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +39 -11
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +33 -10
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +33 -10
- nautobot/tenancy/tables.py +2 -0
- nautobot/virtualization/tests/test_views.py +1 -1
- nautobot/wireless/forms.py +0 -1
- nautobot/wireless/models.py +1 -1
- nautobot/wireless/navigation.py +1 -1
- nautobot/wireless/tables.py +18 -3
- {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/METADATA +4 -4
- {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/RECORD +439 -419
- /nautobot/dcim/templates/dcim/{platform_edit.html → platform_create.html} +0 -0
- /nautobot/extras/test_jobs/{pass.py → pass_job.py} +0 -0
- {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/NOTICE +0 -0
- {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/WHEEL +0 -0
- {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py
CHANGED
|
@@ -16,6 +16,7 @@ from django.forms import (
|
|
|
16
16
|
MultipleHiddenInput,
|
|
17
17
|
)
|
|
18
18
|
from django.shortcuts import get_object_or_404, HttpResponse, redirect, render
|
|
19
|
+
from django.template.loader import render_to_string
|
|
19
20
|
from django.urls import reverse
|
|
20
21
|
from django.utils.encoding import iri_to_uri
|
|
21
22
|
from django.utils.functional import cached_property
|
|
@@ -69,8 +70,9 @@ from nautobot.wireless.models import (
|
|
|
69
70
|
ControllerManagedDeviceGroupWirelessNetworkAssignment,
|
|
70
71
|
)
|
|
71
72
|
from nautobot.wireless.tables import (
|
|
73
|
+
BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
72
74
|
ControllerManagedDeviceGroupRadioProfileAssignmentTable,
|
|
73
|
-
|
|
75
|
+
DeviceGroupWirelessNetworkTable,
|
|
74
76
|
RadioProfileTable,
|
|
75
77
|
)
|
|
76
78
|
|
|
@@ -106,6 +108,7 @@ from .models import (
|
|
|
106
108
|
Module,
|
|
107
109
|
ModuleBay,
|
|
108
110
|
ModuleBayTemplate,
|
|
111
|
+
ModuleFamily,
|
|
109
112
|
ModuleType,
|
|
110
113
|
PathEndpoint,
|
|
111
114
|
Platform,
|
|
@@ -251,21 +254,22 @@ class LocationTypeUIViewSet(NautobotUIViewSet):
|
|
|
251
254
|
#
|
|
252
255
|
|
|
253
256
|
|
|
254
|
-
class
|
|
255
|
-
queryset = Location.objects.all()
|
|
256
|
-
filterset = filters.LocationFilterSet
|
|
257
|
-
filterset_form = forms.LocationFilterForm
|
|
258
|
-
table = tables.LocationTable
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
class LocationView(generic.ObjectView):
|
|
257
|
+
class LocationUIViewSet(NautobotUIViewSet):
|
|
262
258
|
# We aren't accessing tree fields anywhere so this is safe (note that `parent` itself is a normal foreign
|
|
263
259
|
# key, not a tree field). If we ever do access tree fields, this will perform worse, because django will
|
|
264
260
|
# automatically issue a second query (similar to behavior for
|
|
265
261
|
# https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.only)
|
|
266
|
-
queryset = Location.objects.without_tree_fields().
|
|
262
|
+
queryset = Location.objects.without_tree_fields().select_related("location_type", "parent", "tenant")
|
|
263
|
+
filterset_class = filters.LocationFilterSet
|
|
264
|
+
filterset_form_class = forms.LocationFilterForm
|
|
265
|
+
table_class = tables.LocationTable
|
|
266
|
+
form_class = forms.LocationForm
|
|
267
|
+
bulk_update_form_class = forms.LocationBulkEditForm
|
|
268
|
+
serializer_class = serializers.LocationSerializer
|
|
267
269
|
|
|
268
270
|
def get_extra_context(self, request, instance):
|
|
271
|
+
if instance is None:
|
|
272
|
+
return super().get_extra_context(request, instance)
|
|
269
273
|
related_locations = (
|
|
270
274
|
instance.descendants(include_self=True).restrict(request.user, "view").values_list("pk", flat=True)
|
|
271
275
|
)
|
|
@@ -305,7 +309,6 @@ class LocationView(generic.ObjectView):
|
|
|
305
309
|
)
|
|
306
310
|
|
|
307
311
|
children_table = tables.LocationTable(children, hide_hierarchy_ui=True)
|
|
308
|
-
|
|
309
312
|
paginate = {
|
|
310
313
|
"paginator_class": EnhancedPaginator,
|
|
311
314
|
"per_page": get_paginate_count(request),
|
|
@@ -323,34 +326,6 @@ class LocationView(generic.ObjectView):
|
|
|
323
326
|
}
|
|
324
327
|
|
|
325
328
|
|
|
326
|
-
class LocationEditView(generic.ObjectEditView):
|
|
327
|
-
queryset = Location.objects.all()
|
|
328
|
-
model_form = forms.LocationForm
|
|
329
|
-
template_name = "dcim/location_edit.html"
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
class LocationDeleteView(generic.ObjectDeleteView):
|
|
333
|
-
queryset = Location.objects.all()
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
class LocationBulkEditView(generic.BulkEditView):
|
|
337
|
-
queryset = Location.objects.select_related("location_type", "parent", "tenant")
|
|
338
|
-
filterset = filters.LocationFilterSet
|
|
339
|
-
table = tables.LocationTable
|
|
340
|
-
form = forms.LocationBulkEditForm
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
class LocationBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
|
|
344
|
-
queryset = Location.objects.all()
|
|
345
|
-
table = tables.LocationTable
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
class LocationBulkDeleteView(generic.BulkDeleteView):
|
|
349
|
-
queryset = Location.objects.select_related("location_type", "parent", "tenant")
|
|
350
|
-
filterset = filters.LocationFilterSet
|
|
351
|
-
table = tables.LocationTable
|
|
352
|
-
|
|
353
|
-
|
|
354
329
|
class MigrateLocationDataToContactView(generic.ObjectEditView):
|
|
355
330
|
queryset = Location.objects.all()
|
|
356
331
|
model_form = LocationMigrateDataToContactForm
|
|
@@ -1623,7 +1598,14 @@ class ModuleBayTemplateUIViewSet(
|
|
|
1623
1598
|
return parent.display
|
|
1624
1599
|
return ""
|
|
1625
1600
|
|
|
1626
|
-
@action(
|
|
1601
|
+
@action(
|
|
1602
|
+
detail=False,
|
|
1603
|
+
methods=["GET", "POST"],
|
|
1604
|
+
url_path="rename",
|
|
1605
|
+
url_name="bulk_rename",
|
|
1606
|
+
custom_view_base_action="change",
|
|
1607
|
+
custom_view_additional_permissions=["dcim.change_modulebaytemplate"],
|
|
1608
|
+
)
|
|
1627
1609
|
def bulk_rename(self, request, *args, **kwargs):
|
|
1628
1610
|
return self._bulk_rename(request, *args, **kwargs)
|
|
1629
1611
|
|
|
@@ -2256,7 +2238,7 @@ class DeviceWirelessView(generic.ObjectView):
|
|
|
2256
2238
|
wireless_networks = ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.filter(
|
|
2257
2239
|
controller_managed_device_group=controller_managed_device_group
|
|
2258
2240
|
).select_related("wireless_network", "controller_managed_device_group", "vlan")
|
|
2259
|
-
wireless_networks_table =
|
|
2241
|
+
wireless_networks_table = BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable(
|
|
2260
2242
|
data=wireless_networks, user=request.user, orderable=False
|
|
2261
2243
|
)
|
|
2262
2244
|
wireless_networks_table.columns.hide("controller_managed_device_group")
|
|
@@ -2393,35 +2375,6 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2393
2375
|
table_class = tables.ModuleTable
|
|
2394
2376
|
component_model = None
|
|
2395
2377
|
|
|
2396
|
-
def get_action(self):
|
|
2397
|
-
if self.component_model:
|
|
2398
|
-
method = self.request.method.lower()
|
|
2399
|
-
if method == "get":
|
|
2400
|
-
return "view"
|
|
2401
|
-
else:
|
|
2402
|
-
return "change"
|
|
2403
|
-
|
|
2404
|
-
return super().get_action()
|
|
2405
|
-
|
|
2406
|
-
def get_required_permission(self):
|
|
2407
|
-
# TODO: standardize a pattern for permissions enforcement on custom actions
|
|
2408
|
-
if self.component_model:
|
|
2409
|
-
model = self.component_model
|
|
2410
|
-
method = self.request.method.lower()
|
|
2411
|
-
if method == "get":
|
|
2412
|
-
component_action = "view"
|
|
2413
|
-
permissions = [*self.get_permissions_for_model(model, [component_action]), "dcim.view_module"]
|
|
2414
|
-
elif self.action.startswith("bulk_add"):
|
|
2415
|
-
component_action = "add"
|
|
2416
|
-
permissions = [*self.get_permissions_for_model(model, [component_action]), "dcim.change_module"]
|
|
2417
|
-
else:
|
|
2418
|
-
component_action = "change"
|
|
2419
|
-
permissions = [*self.get_permissions_for_model(model, [component_action]), "dcim.change_module"]
|
|
2420
|
-
|
|
2421
|
-
return permissions
|
|
2422
|
-
|
|
2423
|
-
return super().get_required_permission()
|
|
2424
|
-
|
|
2425
2378
|
def get_extra_context(self, request, instance):
|
|
2426
2379
|
context = super().get_extra_context(request, instance)
|
|
2427
2380
|
if instance:
|
|
@@ -2448,7 +2401,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2448
2401
|
|
|
2449
2402
|
return active_parent_tab
|
|
2450
2403
|
|
|
2451
|
-
@action(
|
|
2404
|
+
@action(
|
|
2405
|
+
detail=True,
|
|
2406
|
+
url_path="console-ports",
|
|
2407
|
+
component_model=ConsolePort,
|
|
2408
|
+
custom_view_base_action="view",
|
|
2409
|
+
custom_view_additional_permissions=["dcim.view_consoleport"],
|
|
2410
|
+
)
|
|
2452
2411
|
def consoleports(self, request, *args, **kwargs):
|
|
2453
2412
|
instance = self.get_object()
|
|
2454
2413
|
consoleports = (
|
|
@@ -2467,7 +2426,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2467
2426
|
}
|
|
2468
2427
|
)
|
|
2469
2428
|
|
|
2470
|
-
@action(
|
|
2429
|
+
@action(
|
|
2430
|
+
detail=True,
|
|
2431
|
+
url_path="console-server-ports",
|
|
2432
|
+
component_model=ConsoleServerPort,
|
|
2433
|
+
custom_view_base_action="view",
|
|
2434
|
+
custom_view_additional_permissions=["dcim.view_consoleserverport"],
|
|
2435
|
+
)
|
|
2471
2436
|
def consoleserverports(self, request, *args, **kwargs):
|
|
2472
2437
|
instance = self.get_object()
|
|
2473
2438
|
consoleserverports = (
|
|
@@ -2490,7 +2455,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2490
2455
|
}
|
|
2491
2456
|
)
|
|
2492
2457
|
|
|
2493
|
-
@action(
|
|
2458
|
+
@action(
|
|
2459
|
+
detail=True,
|
|
2460
|
+
url_path="power-ports",
|
|
2461
|
+
component_model=PowerPort,
|
|
2462
|
+
custom_view_base_action="view",
|
|
2463
|
+
custom_view_additional_permissions=["dcim.view_powerport"],
|
|
2464
|
+
)
|
|
2494
2465
|
def powerports(self, request, *args, **kwargs):
|
|
2495
2466
|
instance = self.get_object()
|
|
2496
2467
|
powerports = (
|
|
@@ -2511,7 +2482,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2511
2482
|
}
|
|
2512
2483
|
)
|
|
2513
2484
|
|
|
2514
|
-
@action(
|
|
2485
|
+
@action(
|
|
2486
|
+
detail=True,
|
|
2487
|
+
url_path="power-outlets",
|
|
2488
|
+
component_model=PowerOutlet,
|
|
2489
|
+
custom_view_base_action="view",
|
|
2490
|
+
custom_view_additional_permissions=["dcim.view_poweroutlet"],
|
|
2491
|
+
)
|
|
2515
2492
|
def poweroutlets(self, request, *args, **kwargs):
|
|
2516
2493
|
instance = self.get_object()
|
|
2517
2494
|
poweroutlets = (
|
|
@@ -2532,7 +2509,12 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2532
2509
|
}
|
|
2533
2510
|
)
|
|
2534
2511
|
|
|
2535
|
-
@action(
|
|
2512
|
+
@action(
|
|
2513
|
+
detail=True,
|
|
2514
|
+
component_model=Interface,
|
|
2515
|
+
custom_view_base_action="view",
|
|
2516
|
+
custom_view_additional_permissions=["dcim.view_interface"],
|
|
2517
|
+
)
|
|
2536
2518
|
def interfaces(self, request, *args, **kwargs):
|
|
2537
2519
|
instance = self.get_object()
|
|
2538
2520
|
interfaces = (
|
|
@@ -2558,7 +2540,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2558
2540
|
}
|
|
2559
2541
|
)
|
|
2560
2542
|
|
|
2561
|
-
@action(
|
|
2543
|
+
@action(
|
|
2544
|
+
detail=True,
|
|
2545
|
+
url_path="front-ports",
|
|
2546
|
+
component_model=FrontPort,
|
|
2547
|
+
custom_view_base_action="view",
|
|
2548
|
+
custom_view_additional_permissions=["dcim.view_frontport"],
|
|
2549
|
+
)
|
|
2562
2550
|
def frontports(self, request, *args, **kwargs):
|
|
2563
2551
|
instance = self.get_object()
|
|
2564
2552
|
frontports = instance.front_ports.restrict(request.user, "view").select_related("cable", "rear_port")
|
|
@@ -2575,7 +2563,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2575
2563
|
},
|
|
2576
2564
|
)
|
|
2577
2565
|
|
|
2578
|
-
@action(
|
|
2566
|
+
@action(
|
|
2567
|
+
detail=True,
|
|
2568
|
+
url_path="rear-ports",
|
|
2569
|
+
component_model=RearPort,
|
|
2570
|
+
custom_view_base_action="view",
|
|
2571
|
+
custom_view_additional_permissions=["dcim.view_rearport"],
|
|
2572
|
+
)
|
|
2579
2573
|
def rearports(self, request, *args, **kwargs):
|
|
2580
2574
|
instance = self.get_object()
|
|
2581
2575
|
rearports = instance.rear_ports.restrict(request.user, "view").select_related("cable")
|
|
@@ -2592,7 +2586,13 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2592
2586
|
}
|
|
2593
2587
|
)
|
|
2594
2588
|
|
|
2595
|
-
@action(
|
|
2589
|
+
@action(
|
|
2590
|
+
detail=True,
|
|
2591
|
+
url_path="module-bays",
|
|
2592
|
+
component_model=ModuleBay,
|
|
2593
|
+
custom_view_base_action="view",
|
|
2594
|
+
custom_view_additional_permissions=["dcim.view_modulebay"],
|
|
2595
|
+
)
|
|
2596
2596
|
def modulebays(self, request, *args, **kwargs):
|
|
2597
2597
|
instance = self.get_object()
|
|
2598
2598
|
modulebays = instance.module_bays.restrict(request.user, "view").prefetch_related(
|
|
@@ -2615,6 +2615,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2615
2615
|
url_path="console-ports/add",
|
|
2616
2616
|
url_name="bulk_add_consoleport",
|
|
2617
2617
|
component_model=ConsolePort,
|
|
2618
|
+
custom_view_base_action="change",
|
|
2619
|
+
custom_view_additional_permissions=["dcim.add_consoleport"],
|
|
2618
2620
|
)
|
|
2619
2621
|
def bulk_add_consoleport(self, request, *args, **kwargs):
|
|
2620
2622
|
return self._bulk_component_create(
|
|
@@ -2629,6 +2631,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2629
2631
|
url_path="console-server-ports/add",
|
|
2630
2632
|
url_name="bulk_add_consoleserverport",
|
|
2631
2633
|
component_model=ConsoleServerPort,
|
|
2634
|
+
custom_view_base_action="change",
|
|
2635
|
+
custom_view_additional_permissions=["dcim.add_consoleserverport"],
|
|
2632
2636
|
)
|
|
2633
2637
|
def bulk_add_consoleserverport(self, request, *args, **kwargs):
|
|
2634
2638
|
return self._bulk_component_create(
|
|
@@ -2643,6 +2647,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2643
2647
|
url_path="power-ports/add",
|
|
2644
2648
|
url_name="bulk_add_powerport",
|
|
2645
2649
|
component_model=PowerPort,
|
|
2650
|
+
custom_view_base_action="change",
|
|
2651
|
+
custom_view_additional_permissions=["dcim.add_powerport"],
|
|
2646
2652
|
)
|
|
2647
2653
|
def bulk_add_powerport(self, request, *args, **kwargs):
|
|
2648
2654
|
return self._bulk_component_create(
|
|
@@ -2657,6 +2663,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2657
2663
|
url_path="power-outlets/add",
|
|
2658
2664
|
url_name="bulk_add_poweroutlet",
|
|
2659
2665
|
component_model=PowerOutlet,
|
|
2666
|
+
custom_view_base_action="change",
|
|
2667
|
+
custom_view_additional_permissions=["dcim.add_poweroutlet"],
|
|
2660
2668
|
)
|
|
2661
2669
|
def bulk_add_poweroutlet(self, request, *args, **kwargs):
|
|
2662
2670
|
return self._bulk_component_create(
|
|
@@ -2671,6 +2679,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2671
2679
|
url_path="interfaces/add",
|
|
2672
2680
|
url_name="bulk_add_interface",
|
|
2673
2681
|
component_model=Interface,
|
|
2682
|
+
custom_view_base_action="change",
|
|
2683
|
+
custom_view_additional_permissions=["dcim.add_interface"],
|
|
2674
2684
|
)
|
|
2675
2685
|
def bulk_add_interface(self, request, *args, **kwargs):
|
|
2676
2686
|
return self._bulk_component_create(
|
|
@@ -2685,6 +2695,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2685
2695
|
url_path="rear-ports/add",
|
|
2686
2696
|
url_name="bulk_add_rearport",
|
|
2687
2697
|
component_model=RearPort,
|
|
2698
|
+
custom_view_base_action="change",
|
|
2699
|
+
custom_view_additional_permissions=["dcim.add_rearport"],
|
|
2688
2700
|
)
|
|
2689
2701
|
def bulk_add_rearport(self, request, *args, **kwargs):
|
|
2690
2702
|
return self._bulk_component_create(
|
|
@@ -2699,6 +2711,8 @@ class ModuleUIViewSet(BulkComponentCreateUIViewSetMixin, NautobotUIViewSet):
|
|
|
2699
2711
|
url_path="module-bays/add",
|
|
2700
2712
|
url_name="bulk_add_modulebay",
|
|
2701
2713
|
component_model=ModuleBay,
|
|
2714
|
+
custom_view_base_action="change",
|
|
2715
|
+
custom_view_additional_permissions=["dcim.add_modulebay"],
|
|
2702
2716
|
)
|
|
2703
2717
|
def bulk_add_modulebay(self, request, *args, **kwargs):
|
|
2704
2718
|
return self._bulk_component_create(
|
|
@@ -3394,7 +3408,6 @@ class ModuleBayUIViewSet(ModuleBayCommonViewSetMixin, NautobotUIViewSet):
|
|
|
3394
3408
|
serializer_class = serializers.ModuleBaySerializer
|
|
3395
3409
|
table_class = tables.ModuleBayTable
|
|
3396
3410
|
create_template_name = "dcim/device_component_add.html"
|
|
3397
|
-
|
|
3398
3411
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
3399
3412
|
panels=(
|
|
3400
3413
|
object_detail.ObjectFieldsPanel(
|
|
@@ -3439,7 +3452,14 @@ class ModuleBayUIViewSet(ModuleBayCommonViewSetMixin, NautobotUIViewSet):
|
|
|
3439
3452
|
return parent.display
|
|
3440
3453
|
return ""
|
|
3441
3454
|
|
|
3442
|
-
@action(
|
|
3455
|
+
@action(
|
|
3456
|
+
detail=False,
|
|
3457
|
+
methods=["GET", "POST"],
|
|
3458
|
+
url_path="rename",
|
|
3459
|
+
url_name="bulk_rename",
|
|
3460
|
+
custom_view_base_action="change",
|
|
3461
|
+
custom_view_additional_permissions=["dcim.change_modulebay"],
|
|
3462
|
+
)
|
|
3443
3463
|
def bulk_rename(self, request, *args, **kwargs):
|
|
3444
3464
|
return self._bulk_rename(request, *args, **kwargs)
|
|
3445
3465
|
|
|
@@ -3887,151 +3907,102 @@ class InterfaceConnectionsListView(ConnectionsListView):
|
|
|
3887
3907
|
#
|
|
3888
3908
|
|
|
3889
3909
|
|
|
3890
|
-
class
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
class VirtualChassisView(generic.ObjectView):
|
|
3910
|
+
class VirtualChassisUIViewSet(NautobotUIViewSet):
|
|
3911
|
+
bulk_update_form_class = forms.VirtualChassisBulkEditForm
|
|
3912
|
+
filterset_class = filters.VirtualChassisFilterSet
|
|
3913
|
+
filterset_form_class = forms.VirtualChassisFilterForm
|
|
3914
|
+
form_class = forms.VirtualChassisCreateForm
|
|
3915
|
+
serializer_class = serializers.VirtualChassisSerializer
|
|
3916
|
+
table_class = tables.VirtualChassisTable
|
|
3898
3917
|
queryset = VirtualChassis.objects.all()
|
|
3899
3918
|
|
|
3900
3919
|
def get_extra_context(self, request, instance):
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
return {"members": members, **super().get_extra_context(request, instance)}
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
class VirtualChassisCreateView(generic.ObjectEditView):
|
|
3907
|
-
queryset = VirtualChassis.objects.all()
|
|
3908
|
-
model_form = forms.VirtualChassisCreateForm
|
|
3909
|
-
template_name = "dcim/virtualchassis_add.html"
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
|
|
3913
|
-
queryset = VirtualChassis.objects.all()
|
|
3914
|
-
|
|
3915
|
-
def get_required_permission(self):
|
|
3916
|
-
return "dcim.change_virtualchassis"
|
|
3917
|
-
|
|
3918
|
-
def get(self, request, pk):
|
|
3919
|
-
virtual_chassis = get_object_or_404(self.queryset, pk=pk)
|
|
3920
|
-
VCMemberFormSet = modelformset_factory(
|
|
3921
|
-
model=Device,
|
|
3922
|
-
form=forms.DeviceVCMembershipForm,
|
|
3923
|
-
formset=forms.BaseVCMemberFormSet,
|
|
3924
|
-
extra=0,
|
|
3925
|
-
)
|
|
3926
|
-
members_queryset = virtual_chassis.members.select_related("rack").order_by("vc_position")
|
|
3927
|
-
|
|
3928
|
-
vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
|
|
3929
|
-
vc_form.fields["master"].queryset = members_queryset
|
|
3930
|
-
formset = VCMemberFormSet(queryset=members_queryset)
|
|
3931
|
-
|
|
3932
|
-
return render(
|
|
3933
|
-
request,
|
|
3934
|
-
"dcim/virtualchassis_edit.html",
|
|
3935
|
-
{
|
|
3936
|
-
"vc_form": vc_form,
|
|
3937
|
-
"formset": formset,
|
|
3938
|
-
"return_url": self.get_return_url(request, virtual_chassis),
|
|
3939
|
-
},
|
|
3940
|
-
)
|
|
3941
|
-
|
|
3942
|
-
def post(self, request, pk):
|
|
3943
|
-
virtual_chassis = get_object_or_404(self.queryset, pk=pk)
|
|
3944
|
-
VCMemberFormSet = modelformset_factory(
|
|
3945
|
-
model=Device,
|
|
3946
|
-
form=forms.DeviceVCMembershipForm,
|
|
3947
|
-
formset=forms.BaseVCMemberFormSet,
|
|
3948
|
-
extra=0,
|
|
3949
|
-
)
|
|
3950
|
-
members_queryset = virtual_chassis.members.select_related("rack").order_by("vc_position")
|
|
3951
|
-
|
|
3952
|
-
vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
|
|
3953
|
-
vc_form.fields["master"].queryset = members_queryset
|
|
3954
|
-
formset = VCMemberFormSet(request.POST, queryset=members_queryset)
|
|
3955
|
-
|
|
3956
|
-
if vc_form.is_valid() and formset.is_valid():
|
|
3957
|
-
with transaction.atomic():
|
|
3958
|
-
# Save the VirtualChassis
|
|
3959
|
-
vc_form.save()
|
|
3960
|
-
|
|
3961
|
-
# Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
|
|
3962
|
-
# duplicate positions. Then save each member instance.
|
|
3963
|
-
members = formset.save(commit=False)
|
|
3964
|
-
devices = Device.objects.filter(pk__in=[m.pk for m in members])
|
|
3965
|
-
for device in devices:
|
|
3966
|
-
device.vc_position = None
|
|
3967
|
-
device.save()
|
|
3968
|
-
for member in members:
|
|
3969
|
-
member.save()
|
|
3970
|
-
|
|
3971
|
-
return redirect(virtual_chassis.get_absolute_url())
|
|
3972
|
-
|
|
3973
|
-
return render(
|
|
3974
|
-
request,
|
|
3975
|
-
"dcim/virtualchassis_edit.html",
|
|
3976
|
-
{
|
|
3977
|
-
"vc_form": vc_form,
|
|
3978
|
-
"formset": formset,
|
|
3979
|
-
"return_url": self.get_return_url(request, virtual_chassis),
|
|
3980
|
-
},
|
|
3981
|
-
)
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
class VirtualChassisDeleteView(generic.ObjectDeleteView):
|
|
3985
|
-
queryset = VirtualChassis.objects.all()
|
|
3920
|
+
context = super().get_extra_context(request, instance)
|
|
3986
3921
|
|
|
3922
|
+
if self.action == "update":
|
|
3923
|
+
VCMemberFormSet = modelformset_factory(
|
|
3924
|
+
model=Device,
|
|
3925
|
+
form=forms.DeviceVCMembershipForm,
|
|
3926
|
+
formset=forms.BaseVCMemberFormSet,
|
|
3927
|
+
extra=0,
|
|
3928
|
+
)
|
|
3929
|
+
members_queryset = instance.members.select_related("rack").order_by("vc_position")
|
|
3987
3930
|
|
|
3988
|
-
|
|
3989
|
-
|
|
3931
|
+
if request.method == "POST":
|
|
3932
|
+
formset = VCMemberFormSet(request.POST, queryset=members_queryset)
|
|
3933
|
+
else:
|
|
3934
|
+
formset = VCMemberFormSet(queryset=members_queryset)
|
|
3990
3935
|
|
|
3991
|
-
|
|
3992
|
-
|
|
3936
|
+
vc_form = forms.VirtualChassisForm(instance=instance)
|
|
3937
|
+
vc_form.fields["master"].queryset = members_queryset
|
|
3993
3938
|
|
|
3994
|
-
|
|
3995
|
-
|
|
3939
|
+
context.update(
|
|
3940
|
+
{
|
|
3941
|
+
"formset": formset,
|
|
3942
|
+
"vc_form": vc_form,
|
|
3943
|
+
"return_url": self.get_return_url(request, instance),
|
|
3944
|
+
}
|
|
3945
|
+
)
|
|
3996
3946
|
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
3947
|
+
elif self.action == "retrieve":
|
|
3948
|
+
members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
|
|
3949
|
+
context.update({"members": members})
|
|
4000
3950
|
|
|
4001
|
-
return
|
|
4002
|
-
request,
|
|
4003
|
-
"dcim/virtualchassis_add_member.html",
|
|
4004
|
-
{
|
|
4005
|
-
"virtual_chassis": virtual_chassis,
|
|
4006
|
-
"member_select_form": member_select_form,
|
|
4007
|
-
"membership_form": membership_form,
|
|
4008
|
-
"return_url": self.get_return_url(request, virtual_chassis),
|
|
4009
|
-
},
|
|
4010
|
-
)
|
|
3951
|
+
return context
|
|
4011
3952
|
|
|
4012
|
-
def
|
|
4013
|
-
|
|
3953
|
+
def form_save(self, form, **kwargs):
|
|
3954
|
+
obj = super().form_save(form, **kwargs)
|
|
4014
3955
|
|
|
4015
|
-
|
|
3956
|
+
if self.action == "update":
|
|
3957
|
+
context = self.get_extra_context(self.request, obj)
|
|
3958
|
+
formset = context.get("formset")
|
|
4016
3959
|
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
3960
|
+
if formset.is_valid():
|
|
3961
|
+
with transaction.atomic():
|
|
3962
|
+
members = formset.save(commit=False)
|
|
3963
|
+
# Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
|
|
3964
|
+
# duplicate positions. Then save each member instance.
|
|
3965
|
+
Device.objects.filter(pk__in=[m.pk for m in members]).update(vc_position=None)
|
|
3966
|
+
for member in members:
|
|
3967
|
+
member.save()
|
|
3968
|
+
else:
|
|
3969
|
+
raise ValidationError(formset.errors)
|
|
4022
3970
|
|
|
4023
|
-
|
|
4024
|
-
membership_form.save()
|
|
4025
|
-
msg = format_html('Added member <a href="{}">{}</a>', device.get_absolute_url(), device)
|
|
4026
|
-
messages.success(request, msg)
|
|
3971
|
+
return obj
|
|
4027
3972
|
|
|
4028
|
-
|
|
4029
|
-
|
|
3973
|
+
@action(
|
|
3974
|
+
detail=True,
|
|
3975
|
+
methods=["get", "post"],
|
|
3976
|
+
url_path="add-member",
|
|
3977
|
+
url_name="add_member",
|
|
3978
|
+
custom_view_base_action="change",
|
|
3979
|
+
custom_view_additional_permissions=["dcim.change_virtualchassis"],
|
|
3980
|
+
)
|
|
3981
|
+
def add_member(self, request, pk=None):
|
|
3982
|
+
virtual_chassis = self.get_object()
|
|
4030
3983
|
|
|
4031
|
-
|
|
3984
|
+
if request.method == "POST":
|
|
3985
|
+
member_select_form = forms.VCMemberSelectForm(request.POST)
|
|
3986
|
+
if member_select_form.is_valid():
|
|
3987
|
+
device = member_select_form.cleaned_data["device"]
|
|
3988
|
+
device.virtual_chassis = virtual_chassis
|
|
3989
|
+
data = {k: request.POST[k] for k in ["vc_position", "vc_priority"]}
|
|
3990
|
+
membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
|
|
3991
|
+
|
|
3992
|
+
if membership_form.is_valid():
|
|
3993
|
+
membership_form.save()
|
|
3994
|
+
msg = format_html('Added member <a href="{}">{}</a>', device.get_absolute_url(), device)
|
|
3995
|
+
messages.success(request, msg)
|
|
4032
3996
|
|
|
3997
|
+
if "_addanother" in request.POST:
|
|
3998
|
+
return redirect(request.get_full_path())
|
|
3999
|
+
return redirect(self.get_return_url(request, device))
|
|
4000
|
+
else:
|
|
4001
|
+
membership_form = forms.DeviceVCMembershipForm(data=request.POST)
|
|
4033
4002
|
else:
|
|
4034
|
-
|
|
4003
|
+
initial_data = {k: request.GET[k] for k in request.GET}
|
|
4004
|
+
member_select_form = forms.VCMemberSelectForm(initial=initial_data)
|
|
4005
|
+
membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
|
|
4035
4006
|
|
|
4036
4007
|
return render(
|
|
4037
4008
|
request,
|
|
@@ -4100,24 +4071,6 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
|
|
|
4100
4071
|
)
|
|
4101
4072
|
|
|
4102
4073
|
|
|
4103
|
-
class VirtualChassisBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
|
|
4104
|
-
queryset = VirtualChassis.objects.all()
|
|
4105
|
-
table = tables.VirtualChassisTable
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
class VirtualChassisBulkEditView(generic.BulkEditView):
|
|
4109
|
-
queryset = VirtualChassis.objects.all()
|
|
4110
|
-
filterset = filters.VirtualChassisFilterSet
|
|
4111
|
-
table = tables.VirtualChassisTable
|
|
4112
|
-
form = forms.VirtualChassisBulkEditForm
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
|
|
4116
|
-
queryset = VirtualChassis.objects.all()
|
|
4117
|
-
filterset = filters.VirtualChassisFilterSet
|
|
4118
|
-
table = tables.VirtualChassisTable
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
4074
|
#
|
|
4122
4075
|
# Power panels
|
|
4123
4076
|
#
|
|
@@ -4159,6 +4112,28 @@ class PowerPanelUIViewSet(NautobotUIViewSet):
|
|
|
4159
4112
|
#
|
|
4160
4113
|
# Power feeds
|
|
4161
4114
|
#
|
|
4115
|
+
|
|
4116
|
+
|
|
4117
|
+
class CustomPowerFeedKeyValueTablePanel(object_detail.KeyValueTablePanel):
|
|
4118
|
+
"""Custom panel to render PowerFeed utilization graph cleanly."""
|
|
4119
|
+
|
|
4120
|
+
def render_value(self, key, value, context):
|
|
4121
|
+
if key == "Utilization (Allocated)":
|
|
4122
|
+
if not value or not isinstance(value, tuple) or len(value) != 2:
|
|
4123
|
+
return helpers.placeholder(None)
|
|
4124
|
+
allocated, available = value
|
|
4125
|
+
if available <= 0:
|
|
4126
|
+
return f"{allocated}VA / {available}VA"
|
|
4127
|
+
graph_html = render_to_string(
|
|
4128
|
+
"utilities/templatetags/utilization_graph.html",
|
|
4129
|
+
helpers.utilization_graph_raw_data(allocated, available),
|
|
4130
|
+
)
|
|
4131
|
+
return format_html("{}VA / {}VA {}", allocated, available, graph_html)
|
|
4132
|
+
|
|
4133
|
+
# Fall back to default behavior for everything else
|
|
4134
|
+
return super().render_value(key, value, context)
|
|
4135
|
+
|
|
4136
|
+
|
|
4162
4137
|
class PowerFeedUIViewSet(NautobotUIViewSet):
|
|
4163
4138
|
bulk_update_form_class = forms.PowerFeedBulkEditForm
|
|
4164
4139
|
filterset_class = filters.PowerFeedFilterSet
|
|
@@ -4168,6 +4143,137 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
|
|
|
4168
4143
|
serializer_class = serializers.PowerFeedSerializer
|
|
4169
4144
|
table_class = tables.PowerFeedTable
|
|
4170
4145
|
|
|
4146
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
4147
|
+
panels=(
|
|
4148
|
+
CustomPowerFeedKeyValueTablePanel(
|
|
4149
|
+
section=SectionChoices.LEFT_HALF,
|
|
4150
|
+
weight=100,
|
|
4151
|
+
label="Power Feed",
|
|
4152
|
+
context_data_key="powerfeed_data",
|
|
4153
|
+
),
|
|
4154
|
+
object_detail.ObjectFieldsPanel(
|
|
4155
|
+
section=SectionChoices.LEFT_HALF,
|
|
4156
|
+
weight=200,
|
|
4157
|
+
label="Electrical Characteristics",
|
|
4158
|
+
fields=["supply", "voltage", "amperage", "phase", "max_utilization"],
|
|
4159
|
+
value_transforms={
|
|
4160
|
+
"voltage": [lambda v: f"{v}V" if v is not None else helpers.placeholder(v)],
|
|
4161
|
+
"amperage": [lambda a: f"{a}A" if a is not None else helpers.placeholder(a)],
|
|
4162
|
+
"max_utilization": [lambda p: f"{p}%" if p is not None else helpers.placeholder(p)],
|
|
4163
|
+
},
|
|
4164
|
+
),
|
|
4165
|
+
object_detail.KeyValueTablePanel(
|
|
4166
|
+
section=SectionChoices.RIGHT_HALF,
|
|
4167
|
+
weight=300,
|
|
4168
|
+
label="Connection",
|
|
4169
|
+
context_data_key="connection_data",
|
|
4170
|
+
),
|
|
4171
|
+
)
|
|
4172
|
+
)
|
|
4173
|
+
|
|
4174
|
+
def get_extra_context(self, request, instance):
|
|
4175
|
+
context = super().get_extra_context(request, instance)
|
|
4176
|
+
if not instance or self.action != "retrieve":
|
|
4177
|
+
return context
|
|
4178
|
+
|
|
4179
|
+
context["powerfeed_data"] = {
|
|
4180
|
+
"Power Panel": instance.power_panel,
|
|
4181
|
+
"Rack": instance.rack,
|
|
4182
|
+
"Type": self._get_type_html(instance), # Render Type with HTML label
|
|
4183
|
+
"Status": instance.status,
|
|
4184
|
+
"Connected Device": self._get_connected_device_html(instance),
|
|
4185
|
+
"Utilization (Allocated)": self._get_utilization_data(instance),
|
|
4186
|
+
}
|
|
4187
|
+
|
|
4188
|
+
context["connection_data"] = self._get_connection_data(request, instance)
|
|
4189
|
+
return context
|
|
4190
|
+
|
|
4191
|
+
def _get_type_html(self, instance):
|
|
4192
|
+
"""
|
|
4193
|
+
Render the PowerFeed type as a label with the appropriate CSS class.
|
|
4194
|
+
"""
|
|
4195
|
+
|
|
4196
|
+
type_class = instance.get_type_class()
|
|
4197
|
+
return format_html('<span class="label label-{}">{}</span>', type_class, instance.get_type_display())
|
|
4198
|
+
|
|
4199
|
+
def _get_connected_device_html(self, instance):
|
|
4200
|
+
endpoint = getattr(instance, "connected_endpoint", None)
|
|
4201
|
+
if endpoint and endpoint.parent:
|
|
4202
|
+
parent = helpers.hyperlinked_object(endpoint.parent)
|
|
4203
|
+
return format_html("{} ({})", parent, endpoint)
|
|
4204
|
+
return None
|
|
4205
|
+
|
|
4206
|
+
def _get_utilization_data(self, instance):
|
|
4207
|
+
endpoint = getattr(instance, "connected_endpoint", None)
|
|
4208
|
+
if not endpoint or not hasattr(endpoint, "get_power_draw"):
|
|
4209
|
+
return None
|
|
4210
|
+
utilization = endpoint.get_power_draw()
|
|
4211
|
+
if not utilization or "allocated" not in utilization:
|
|
4212
|
+
return None
|
|
4213
|
+
allocated = utilization["allocated"]
|
|
4214
|
+
available = instance.available_power or 0
|
|
4215
|
+
return (allocated, available)
|
|
4216
|
+
|
|
4217
|
+
def _get_connection_data(self, request, instance):
|
|
4218
|
+
if not instance:
|
|
4219
|
+
return {}
|
|
4220
|
+
|
|
4221
|
+
if instance.cable:
|
|
4222
|
+
trace_url = reverse("dcim:powerfeed_trace", kwargs={"pk": instance.pk})
|
|
4223
|
+
cable_html = format_html(
|
|
4224
|
+
'{} <a href="{}" class="btn btn-primary btn-xs" title="Trace">'
|
|
4225
|
+
'<i class="mdi mdi-transit-connection-variant"></i></a>',
|
|
4226
|
+
helpers.hyperlinked_object(instance.cable),
|
|
4227
|
+
trace_url,
|
|
4228
|
+
)
|
|
4229
|
+
|
|
4230
|
+
endpoint = getattr(instance, "connected_endpoint", None)
|
|
4231
|
+
endpoint_data = {}
|
|
4232
|
+
|
|
4233
|
+
if endpoint:
|
|
4234
|
+
endpoint_obj = getattr(endpoint, "device", None) or getattr(endpoint, "module", None)
|
|
4235
|
+
# Removed the unused 'path' variable
|
|
4236
|
+
endpoint_data = {
|
|
4237
|
+
"Device" if getattr(endpoint, "device", None) else "Module": endpoint_obj,
|
|
4238
|
+
"Power Port": endpoint,
|
|
4239
|
+
"Type": endpoint.get_type_display() if hasattr(endpoint, "get_type_display") else None,
|
|
4240
|
+
"Description": endpoint.description,
|
|
4241
|
+
"Path Status": self._get_path_status_html(instance), # Render Path Status dynamically
|
|
4242
|
+
}
|
|
4243
|
+
|
|
4244
|
+
return {
|
|
4245
|
+
"Cable": cable_html,
|
|
4246
|
+
**endpoint_data,
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4249
|
+
if request.user.has_perm("dcim.add_cable"):
|
|
4250
|
+
connect_url = (
|
|
4251
|
+
reverse(
|
|
4252
|
+
"dcim:powerfeed_connect",
|
|
4253
|
+
kwargs={"termination_a_id": instance.pk, "termination_b_type": "power-port"},
|
|
4254
|
+
)
|
|
4255
|
+
+ f"?return_url={instance.get_absolute_url()}"
|
|
4256
|
+
)
|
|
4257
|
+
connect_link = format_html(
|
|
4258
|
+
'<a href="{}" class="btn btn-primary btn-sm pull-right">'
|
|
4259
|
+
'<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect</a>',
|
|
4260
|
+
connect_url,
|
|
4261
|
+
)
|
|
4262
|
+
return {"Connection": format_html("Not connected {}", connect_link)}
|
|
4263
|
+
|
|
4264
|
+
return {"Connection": "Not connected"}
|
|
4265
|
+
|
|
4266
|
+
def _get_path_status_html(self, instance):
|
|
4267
|
+
"""
|
|
4268
|
+
Render the Path Status as a label based on the path status (active or not).
|
|
4269
|
+
"""
|
|
4270
|
+
path_status = (
|
|
4271
|
+
'<span class="label label-success">Reachable</span>'
|
|
4272
|
+
if getattr(instance, "path", None) and instance.path.is_active
|
|
4273
|
+
else '<span class="label label-danger">Not Reachable</span>'
|
|
4274
|
+
)
|
|
4275
|
+
return format_html(path_status) # Safely render HTML
|
|
4276
|
+
|
|
4171
4277
|
|
|
4172
4278
|
class DeviceRedundancyGroupUIViewSet(NautobotUIViewSet):
|
|
4173
4279
|
bulk_update_form_class = forms.DeviceRedundancyGroupBulkEditForm
|
|
@@ -4218,38 +4324,35 @@ class InterfaceRedundancyGroupUIViewSet(NautobotUIViewSet):
|
|
|
4218
4324
|
table_class = tables.InterfaceRedundancyGroupTable
|
|
4219
4325
|
lookup_field = "pk"
|
|
4220
4326
|
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
for column_name in column_sequence:
|
|
4251
|
-
table.columns.show(column_name)
|
|
4252
|
-
return table
|
|
4327
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
4328
|
+
panels=(
|
|
4329
|
+
object_detail.ObjectFieldsPanel(
|
|
4330
|
+
weight=100,
|
|
4331
|
+
section=SectionChoices.LEFT_HALF,
|
|
4332
|
+
fields="__all__",
|
|
4333
|
+
),
|
|
4334
|
+
object_detail.ObjectsTablePanel(
|
|
4335
|
+
weight=200,
|
|
4336
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4337
|
+
table_class=tables.InterfaceRedundancyGroupAssociationTable,
|
|
4338
|
+
table_attribute="interface_redundancy_group_associations",
|
|
4339
|
+
prefetch_related_fields=["interface"],
|
|
4340
|
+
order_by_fields=["priority"],
|
|
4341
|
+
table_title="Interfaces",
|
|
4342
|
+
related_field_name="interface_redundancy_group",
|
|
4343
|
+
include_columns=[
|
|
4344
|
+
"interface__device",
|
|
4345
|
+
"interface",
|
|
4346
|
+
"interface__status",
|
|
4347
|
+
"interface__enabled",
|
|
4348
|
+
"interface__ip_addresses",
|
|
4349
|
+
"interface__type",
|
|
4350
|
+
"interface__description",
|
|
4351
|
+
"interface__label",
|
|
4352
|
+
],
|
|
4353
|
+
),
|
|
4354
|
+
),
|
|
4355
|
+
)
|
|
4253
4356
|
|
|
4254
4357
|
|
|
4255
4358
|
class InterfaceRedundancyGroupAssociationUIViewSet(ObjectEditViewMixin, ObjectDestroyViewMixin):
|
|
@@ -4259,6 +4362,51 @@ class InterfaceRedundancyGroupAssociationUIViewSet(ObjectEditViewMixin, ObjectDe
|
|
|
4259
4362
|
lookup_field = "pk"
|
|
4260
4363
|
|
|
4261
4364
|
|
|
4365
|
+
class ModuleFamilyUIViewSet(NautobotUIViewSet):
|
|
4366
|
+
"""ViewSet for the ModuleFamily model."""
|
|
4367
|
+
|
|
4368
|
+
filterset_class = filters.ModuleFamilyFilterSet
|
|
4369
|
+
filterset_form_class = forms.ModuleFamilyFilterForm
|
|
4370
|
+
form_class = forms.ModuleFamilyForm
|
|
4371
|
+
bulk_update_form_class = forms.ModuleFamilyBulkEditForm
|
|
4372
|
+
queryset = ModuleFamily.objects.all()
|
|
4373
|
+
serializer_class = serializers.ModuleFamilySerializer
|
|
4374
|
+
table_class = tables.ModuleFamilyTable
|
|
4375
|
+
lookup_field = "pk"
|
|
4376
|
+
|
|
4377
|
+
def get_extra_context(self, request, instance):
|
|
4378
|
+
context = super().get_extra_context(request, instance)
|
|
4379
|
+
if not instance:
|
|
4380
|
+
return context
|
|
4381
|
+
|
|
4382
|
+
if self.action == "retrieve":
|
|
4383
|
+
module_types = (
|
|
4384
|
+
ModuleType.objects.restrict(request.user, "view")
|
|
4385
|
+
.filter(module_family=instance)
|
|
4386
|
+
.select_related("manufacturer")
|
|
4387
|
+
)
|
|
4388
|
+
module_type_table = tables.ModuleTypeTable(module_types, orderable=False)
|
|
4389
|
+
|
|
4390
|
+
module_bays = (
|
|
4391
|
+
ModuleBay.objects.restrict(request.user, "view")
|
|
4392
|
+
.filter(module_family=instance)
|
|
4393
|
+
.select_related("parent_device", "parent_module")
|
|
4394
|
+
)
|
|
4395
|
+
module_bay_table = tables.ModuleBayTable(module_bays, orderable=False)
|
|
4396
|
+
|
|
4397
|
+
paginate = {
|
|
4398
|
+
"paginator_class": EnhancedPaginator,
|
|
4399
|
+
"per_page": get_paginate_count(request),
|
|
4400
|
+
}
|
|
4401
|
+
RequestConfig(request, paginate).configure(module_type_table)
|
|
4402
|
+
RequestConfig(request, paginate).configure(module_bay_table)
|
|
4403
|
+
|
|
4404
|
+
context["module_type_table"] = module_type_table
|
|
4405
|
+
context["module_bay_table"] = module_bay_table
|
|
4406
|
+
|
|
4407
|
+
return context
|
|
4408
|
+
|
|
4409
|
+
|
|
4262
4410
|
class DeviceFamilyUIViewSet(NautobotUIViewSet):
|
|
4263
4411
|
filterset_class = filters.DeviceFamilyFilterSet
|
|
4264
4412
|
filterset_form_class = forms.DeviceFamilyFilterForm
|
|
@@ -4308,7 +4456,6 @@ class SoftwareImageFileUIViewSet(NautobotUIViewSet):
|
|
|
4308
4456
|
queryset = SoftwareImageFile.objects.all()
|
|
4309
4457
|
serializer_class = serializers.SoftwareImageFileSerializer
|
|
4310
4458
|
table_class = tables.SoftwareImageFileTable
|
|
4311
|
-
|
|
4312
4459
|
object_detail_content = object_detail.ObjectDetailContent(
|
|
4313
4460
|
panels=(
|
|
4314
4461
|
object_detail.ObjectFieldsPanel(
|
|
@@ -4392,19 +4539,42 @@ class SoftwareImageFileUIViewSet(NautobotUIViewSet):
|
|
|
4392
4539
|
),
|
|
4393
4540
|
)
|
|
4394
4541
|
|
|
4395
|
-
@action(
|
|
4542
|
+
@action(
|
|
4543
|
+
detail=True,
|
|
4544
|
+
url_path="device-types",
|
|
4545
|
+
url_name="device_types",
|
|
4546
|
+
custom_view_base_action="view",
|
|
4547
|
+
custom_view_additional_permissions=["dcim.view_devicetype"],
|
|
4548
|
+
)
|
|
4396
4549
|
def device_types(self, request, *args, **kwargs):
|
|
4397
4550
|
return Response({})
|
|
4398
4551
|
|
|
4399
|
-
@action(
|
|
4552
|
+
@action(
|
|
4553
|
+
detail=True,
|
|
4554
|
+
url_path="devices",
|
|
4555
|
+
custom_view_base_action="view",
|
|
4556
|
+
custom_view_additional_permissions=["dcim.view_device"],
|
|
4557
|
+
)
|
|
4400
4558
|
def devices(self, request, *args, **kwargs):
|
|
4401
4559
|
return Response({})
|
|
4402
4560
|
|
|
4403
|
-
@action(
|
|
4561
|
+
@action(
|
|
4562
|
+
detail=True,
|
|
4563
|
+
url_path="inventory-items",
|
|
4564
|
+
url_name="inventory_items",
|
|
4565
|
+
custom_view_base_action="view",
|
|
4566
|
+
custom_view_additional_permissions=["dcim.view_inventoryitem"],
|
|
4567
|
+
)
|
|
4404
4568
|
def inventory_items(self, request, *args, **kwargs):
|
|
4405
4569
|
return Response({})
|
|
4406
4570
|
|
|
4407
|
-
@action(
|
|
4571
|
+
@action(
|
|
4572
|
+
detail=True,
|
|
4573
|
+
url_path="virtual-machines",
|
|
4574
|
+
url_name="virtual_machines",
|
|
4575
|
+
custom_view_base_action="view",
|
|
4576
|
+
custom_view_additional_permissions=["virtualization.view_virtualmachine"],
|
|
4577
|
+
)
|
|
4408
4578
|
def virtual_machines(self, request, *args, **kwargs):
|
|
4409
4579
|
return Response({})
|
|
4410
4580
|
|
|
@@ -4450,54 +4620,80 @@ class ControllerUIViewSet(NautobotUIViewSet):
|
|
|
4450
4620
|
table_class = tables.ControllerTable
|
|
4451
4621
|
template_name = "dcim/controller_create.html"
|
|
4452
4622
|
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4623
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
4624
|
+
panels=(
|
|
4625
|
+
object_detail.ObjectFieldsPanel(
|
|
4626
|
+
section=SectionChoices.LEFT_HALF,
|
|
4627
|
+
weight=100,
|
|
4628
|
+
fields="__all__",
|
|
4629
|
+
value_transforms={
|
|
4630
|
+
"capabilities": [helpers.label_list],
|
|
4631
|
+
},
|
|
4632
|
+
exclude_fields=[
|
|
4633
|
+
"external_integration",
|
|
4634
|
+
"controller_device",
|
|
4635
|
+
"controller_device_redundancy_group",
|
|
4636
|
+
],
|
|
4637
|
+
),
|
|
4638
|
+
object_detail.ObjectFieldsPanel(
|
|
4639
|
+
section=SectionChoices.RIGHT_HALF,
|
|
4640
|
+
weight=200,
|
|
4641
|
+
label="Integration",
|
|
4642
|
+
fields=[
|
|
4643
|
+
"external_integration",
|
|
4644
|
+
"controller_device",
|
|
4645
|
+
"controller_device_redundancy_group",
|
|
4646
|
+
],
|
|
4647
|
+
),
|
|
4648
|
+
object_detail.ObjectsTablePanel(
|
|
4649
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4650
|
+
weight=100,
|
|
4651
|
+
table_class=tables.DeviceTable,
|
|
4652
|
+
table_title="Managed Devices",
|
|
4653
|
+
table_filter="controller_managed_device_group__controller",
|
|
4654
|
+
include_columns=[
|
|
4655
|
+
"capabilities",
|
|
4656
|
+
"controller_managed_device_group",
|
|
4657
|
+
"manufacturer",
|
|
4658
|
+
],
|
|
4659
|
+
related_field_name="controller",
|
|
4660
|
+
add_button_route=None,
|
|
4661
|
+
),
|
|
4662
|
+
),
|
|
4663
|
+
extra_tabs=(
|
|
4664
|
+
object_detail.DistinctViewTab(
|
|
4665
|
+
weight=700,
|
|
4666
|
+
tab_id="wireless_networks",
|
|
4667
|
+
url_name="dcim:controller_wirelessnetworks",
|
|
4668
|
+
label="Wireless Networks",
|
|
4669
|
+
related_object_attribute="wireless_network_assignments",
|
|
4670
|
+
panels=(
|
|
4671
|
+
object_detail.ObjectsTablePanel(
|
|
4672
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4673
|
+
weight=100,
|
|
4674
|
+
table_title="Wireless Networks",
|
|
4675
|
+
table_class=BaseControllerManagedDeviceGroupWirelessNetworkAssignmentTable,
|
|
4676
|
+
table_filter="controller_managed_device_group__controller",
|
|
4677
|
+
tab_id="wireless_networks",
|
|
4678
|
+
add_button_route=None,
|
|
4679
|
+
select_related_fields=["wireless_network"],
|
|
4680
|
+
exclude_columns=["controller"],
|
|
4681
|
+
),
|
|
4682
|
+
),
|
|
4683
|
+
),
|
|
4684
|
+
),
|
|
4685
|
+
)
|
|
4472
4686
|
|
|
4473
|
-
@action(
|
|
4687
|
+
@action(
|
|
4688
|
+
detail=True,
|
|
4689
|
+
url_path="wireless-networks",
|
|
4690
|
+
url_name="wirelessnetworks",
|
|
4691
|
+
methods=["get"],
|
|
4692
|
+
custom_view_base_action="view",
|
|
4693
|
+
custom_view_additional_permissions=["wireless.view_controllermanageddevicegroupwirelessnetworkassignment"],
|
|
4694
|
+
)
|
|
4474
4695
|
def wirelessnetworks(self, request, *args, **kwargs):
|
|
4475
|
-
|
|
4476
|
-
controller_managed_device_groups = instance.controller_managed_device_groups.restrict(
|
|
4477
|
-
request.user, "view"
|
|
4478
|
-
).values_list("pk", flat=True)
|
|
4479
|
-
wireless_networks = ControllerManagedDeviceGroupWirelessNetworkAssignment.objects.filter(
|
|
4480
|
-
controller_managed_device_group__in=list(controller_managed_device_groups)
|
|
4481
|
-
).select_related("wireless_network")
|
|
4482
|
-
wireless_networks_table = ControllerManagedDeviceGroupWirelessNetworkAssignmentTable(
|
|
4483
|
-
data=wireless_networks, user=request.user, orderable=False
|
|
4484
|
-
)
|
|
4485
|
-
wireless_networks_table.columns.hide("controller")
|
|
4486
|
-
|
|
4487
|
-
RequestConfig(
|
|
4488
|
-
request, paginate={"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
4489
|
-
).configure(wireless_networks_table)
|
|
4490
|
-
|
|
4491
|
-
return Response(
|
|
4492
|
-
{
|
|
4493
|
-
"wireless_networks_table": wireless_networks_table,
|
|
4494
|
-
"active_tab": "wireless-networks",
|
|
4495
|
-
}
|
|
4496
|
-
)
|
|
4497
|
-
|
|
4498
|
-
def get_action(self):
|
|
4499
|
-
"Treat Wireless Networks as the same detail view for permission purposes."
|
|
4500
|
-
return "view" if self.action == "wirelessnetworks" else super().get_action()
|
|
4696
|
+
return Response({})
|
|
4501
4697
|
|
|
4502
4698
|
|
|
4503
4699
|
class ControllerManagedDeviceGroupUIViewSet(NautobotUIViewSet):
|
|
@@ -4509,42 +4705,88 @@ class ControllerManagedDeviceGroupUIViewSet(NautobotUIViewSet):
|
|
|
4509
4705
|
serializer_class = serializers.ControllerManagedDeviceGroupSerializer
|
|
4510
4706
|
table_class = tables.ControllerManagedDeviceGroupTable
|
|
4511
4707
|
template_name = "dcim/controllermanageddevicegroup_create.html"
|
|
4708
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
4709
|
+
panels=(
|
|
4710
|
+
object_detail.ObjectFieldsPanel(
|
|
4711
|
+
section=SectionChoices.LEFT_HALF,
|
|
4712
|
+
weight=100,
|
|
4713
|
+
fields="__all__",
|
|
4714
|
+
value_transforms={
|
|
4715
|
+
"capabilities": [helpers.label_list],
|
|
4716
|
+
},
|
|
4717
|
+
),
|
|
4718
|
+
object_detail.ObjectsTablePanel(
|
|
4719
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4720
|
+
weight=100,
|
|
4721
|
+
table_class=tables.DeviceTable,
|
|
4722
|
+
table_filter="controller_managed_device_group",
|
|
4723
|
+
add_button_route=None,
|
|
4724
|
+
),
|
|
4725
|
+
),
|
|
4726
|
+
extra_tabs=(
|
|
4727
|
+
object_detail.DistinctViewTab(
|
|
4728
|
+
weight=800,
|
|
4729
|
+
tab_id="wireless_networks",
|
|
4730
|
+
label="Wireless Networks",
|
|
4731
|
+
url_name="dcim:controllermanageddevicegroup_wireless_networks",
|
|
4732
|
+
related_object_attribute="wireless_network_assignments",
|
|
4733
|
+
panels=(
|
|
4734
|
+
object_detail.ObjectsTablePanel(
|
|
4735
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4736
|
+
weight=100,
|
|
4737
|
+
table_title="Wireless Networks",
|
|
4738
|
+
table_class=DeviceGroupWirelessNetworkTable,
|
|
4739
|
+
table_filter="controller_managed_device_group",
|
|
4740
|
+
related_field_name="controller_managed_device_groups",
|
|
4741
|
+
tab_id="wireless_networks",
|
|
4742
|
+
add_button_route=None,
|
|
4743
|
+
exclude_columns=["controller_managed_device_group", "controller"],
|
|
4744
|
+
),
|
|
4745
|
+
),
|
|
4746
|
+
),
|
|
4747
|
+
object_detail.DistinctViewTab(
|
|
4748
|
+
weight=900,
|
|
4749
|
+
tab_id="radio_profiles",
|
|
4750
|
+
label="Radio Profiles",
|
|
4751
|
+
url_name="dcim:controllermanageddevicegroup_radio_profiles",
|
|
4752
|
+
related_object_attribute="radio_profiles",
|
|
4753
|
+
panels=(
|
|
4754
|
+
object_detail.ObjectsTablePanel(
|
|
4755
|
+
section=SectionChoices.FULL_WIDTH,
|
|
4756
|
+
weight=100,
|
|
4757
|
+
table_title="Radio Profiles",
|
|
4758
|
+
table_class=RadioProfileTable,
|
|
4759
|
+
table_filter="controller_managed_device_groups",
|
|
4760
|
+
tab_id="radio_profiles",
|
|
4761
|
+
add_button_route=None,
|
|
4762
|
+
),
|
|
4763
|
+
),
|
|
4764
|
+
),
|
|
4765
|
+
),
|
|
4766
|
+
)
|
|
4512
4767
|
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4768
|
+
@action(
|
|
4769
|
+
detail=True,
|
|
4770
|
+
url_path="wireless-networks",
|
|
4771
|
+
url_name="wireless_networks",
|
|
4772
|
+
custom_view_base_action="view",
|
|
4773
|
+
custom_view_additional_permissions=["wireless.view_controllermanageddevicegroupwirelessnetworkassignment"],
|
|
4774
|
+
)
|
|
4775
|
+
def wireless_networks(self, request, *args, **kwargs):
|
|
4776
|
+
return Response({})
|
|
4519
4777
|
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
wireless_networks = instance.wireless_network_assignments.restrict(request.user, "view")
|
|
4530
|
-
wireless_networks_table = ControllerManagedDeviceGroupWirelessNetworkAssignmentTable(wireless_networks)
|
|
4531
|
-
wireless_networks_table.columns.hide("controller_managed_device_group")
|
|
4532
|
-
wireless_networks_table.columns.hide("controller")
|
|
4533
|
-
RequestConfig(
|
|
4534
|
-
request, paginate={"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
4535
|
-
).configure(wireless_networks_table)
|
|
4536
|
-
context["wireless_networks_table"] = wireless_networks_table
|
|
4537
|
-
context["wireless_networks_count"] = wireless_networks.count()
|
|
4538
|
-
|
|
4539
|
-
# Radio Profiles
|
|
4540
|
-
radio_profiles = instance.radio_profiles.restrict(request.user, "view")
|
|
4541
|
-
radio_profiles_table = RadioProfileTable(radio_profiles)
|
|
4542
|
-
RequestConfig(
|
|
4543
|
-
request, paginate={"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
|
|
4544
|
-
).configure(radio_profiles_table)
|
|
4545
|
-
context["radio_profiles_table"] = radio_profiles_table
|
|
4546
|
-
context["radio_profiles_count"] = radio_profiles.count()
|
|
4778
|
+
@action(
|
|
4779
|
+
detail=True,
|
|
4780
|
+
url_path="radio-profiles",
|
|
4781
|
+
url_name="radio_profiles",
|
|
4782
|
+
custom_view_base_action="view",
|
|
4783
|
+
custom_view_additional_permissions=["wireless.view_radioprofile"],
|
|
4784
|
+
)
|
|
4785
|
+
def radio_profiles(self, request, *args, **kwargs):
|
|
4786
|
+
return Response({})
|
|
4547
4787
|
|
|
4788
|
+
def get_extra_context(self, request, instance):
|
|
4789
|
+
context = super().get_extra_context(request, instance)
|
|
4548
4790
|
if self.action in ["create", "update"]:
|
|
4549
4791
|
context["wireless_networks"] = ControllerManagedDeviceGroupWirelessNetworkFormSet(
|
|
4550
4792
|
instance=instance,
|