nautobot 2.4.14__py3-none-any.whl → 2.4.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nautobot/apps/choices.py +8 -0
- nautobot/apps/ui.py +14 -0
- nautobot/core/api/views.py +2 -0
- nautobot/core/choices.py +4 -0
- nautobot/core/filters.py +21 -41
- nautobot/core/management/commands/check_job_approval_status.py +47 -0
- nautobot/core/management/commands/generate_test_data.py +1 -1
- nautobot/core/management/commands/migrate.py +1 -1
- nautobot/core/models/tree_queries.py +17 -0
- nautobot/core/settings.py +2 -2
- nautobot/core/tables.py +25 -2
- nautobot/core/templates/base_django.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/generic/object_list.html +17 -20
- nautobot/core/templates/inc/breadcrumbs.html +14 -0
- nautobot/core/templatetags/buttons.py +2 -4
- nautobot/core/templatetags/helpers.py +29 -6
- nautobot/core/templatetags/ui_framework.py +21 -0
- nautobot/core/testing/filters.py +20 -3
- nautobot/core/testing/forms.py +1 -1
- nautobot/core/tests/integration/test_filters.py +2 -2
- nautobot/core/tests/test_breadcrumbs.py +366 -0
- nautobot/core/tests/test_commands.py +40 -0
- nautobot/core/tests/test_filters.py +51 -1
- nautobot/core/tests/test_forms.py +1 -1
- nautobot/core/tests/test_graphql.py +4 -4
- nautobot/core/tests/test_titles.py +183 -0
- nautobot/core/tests/test_tree_queries.py +30 -0
- nautobot/core/tests/test_views.py +2 -2
- nautobot/core/tests/test_views_utils.py +1 -1
- nautobot/core/ui/breadcrumbs.py +538 -0
- nautobot/core/ui/bulk_buttons.py +53 -0
- nautobot/core/ui/object_detail.py +31 -8
- nautobot/core/ui/titles.py +127 -0
- nautobot/core/ui/utils.py +25 -0
- nautobot/core/utils/migrations.py +1 -1
- nautobot/core/views/__init__.py +1 -1
- nautobot/core/views/mixins.py +26 -1
- nautobot/core/views/renderers.py +20 -2
- nautobot/core/views/utils.py +13 -12
- nautobot/dcim/api/serializers.py +9 -0
- nautobot/dcim/choices.py +53 -0
- nautobot/dcim/filters/__init__.py +15 -3
- nautobot/dcim/forms.py +120 -7
- nautobot/dcim/management/commands/trace_paths.py +1 -1
- nautobot/dcim/migrations/0072_alter_powerfeed_options_and_more.py +97 -0
- nautobot/dcim/models/device_component_templates.py +8 -0
- nautobot/dcim/models/device_components.py +31 -12
- nautobot/dcim/models/devices.py +1 -1
- nautobot/dcim/models/power.py +171 -10
- nautobot/dcim/models/racks.py +7 -4
- nautobot/dcim/tables/devices.py +2 -0
- nautobot/dcim/tables/devicetypes.py +1 -0
- nautobot/dcim/tables/power.py +30 -2
- nautobot/dcim/templates/dcim/device.html +2 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -214
- nautobot/dcim/templates/dcim/location_retrieve.html +2 -2
- nautobot/dcim/templates/dcim/powerfeed_edit.html +8 -0
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -1
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +61 -0
- nautobot/dcim/tests/test_api.py +24 -4
- nautobot/dcim/tests/test_filters.py +91 -13
- nautobot/dcim/tests/test_models.py +262 -0
- nautobot/dcim/tests/test_views.py +20 -12
- nautobot/dcim/utils.py +9 -0
- nautobot/dcim/views.py +390 -77
- nautobot/extras/factory.py +19 -20
- nautobot/extras/filters/__init__.py +3 -2
- nautobot/extras/filters/mixins.py +15 -1
- nautobot/extras/forms/__init__.py +2 -1
- nautobot/extras/forms/forms.py +62 -0
- nautobot/extras/managers.py +4 -1
- nautobot/extras/migrations/0125_jobresult_date_started.py +18 -0
- nautobot/extras/models/customfields.py +1 -2
- nautobot/extras/models/datasources.py +1 -2
- nautobot/extras/models/jobs.py +7 -3
- nautobot/extras/plugins/views.py +24 -1
- nautobot/extras/secrets/__init__.py +1 -1
- nautobot/extras/tables.py +21 -0
- nautobot/extras/templates/extras/customfield.html +2 -129
- nautobot/extras/templates/extras/customfield_edit.html +2 -108
- nautobot/extras/templates/extras/customfield_retrieve.html +129 -0
- nautobot/extras/templates/extras/customfield_update.html +108 -0
- nautobot/extras/templates/extras/inc/jobresult.html +7 -3
- nautobot/extras/templates/extras/jobresult.html +2 -155
- nautobot/extras/templates/extras/jobresult_retrieve.html +155 -0
- nautobot/extras/templates/extras/marketplace.html +5 -6
- nautobot/extras/templates/extras/note.html +2 -53
- nautobot/extras/templates/extras/note_retrieve.html +53 -0
- nautobot/extras/templates/extras/plugins_list.html +5 -6
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +2 -29
- nautobot/extras/templatetags/custom_links.py +2 -2
- nautobot/extras/templatetags/job_buttons.py +1 -1
- nautobot/extras/templatetags/plugins.py +1 -1
- nautobot/extras/tests/integration/test_computedfields.py +2 -2
- nautobot/extras/tests/integration/test_customfields.py +14 -11
- nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
- nautobot/extras/tests/integration/test_notes.py +1 -1
- nautobot/extras/tests/integration/test_plugins.py +6 -6
- nautobot/extras/tests/integration/test_relationships.py +2 -2
- nautobot/extras/tests/test_filters.py +9 -0
- nautobot/extras/tests/test_forms.py +2 -2
- nautobot/extras/tests/test_plugins.py +14 -3
- nautobot/extras/tests/test_relationships.py +7 -7
- nautobot/extras/tests/test_views.py +172 -1
- nautobot/extras/urls.py +3 -59
- nautobot/extras/utils.py +1 -1
- nautobot/extras/views.py +96 -182
- nautobot/ipam/tables.py +8 -15
- nautobot/ipam/tests/migration/test_migrations.py +8 -8
- nautobot/ipam/tests/test_api.py +2 -2
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/project-static/docs/404.html +23 -0
- nautobot/project-static/docs/apps/index.html +23 -0
- nautobot/project-static/docs/apps/nautobot-apps.html +23 -0
- nautobot/project-static/docs/assets/_mkdocstrings.css +44 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +28 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +25 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +128 -20
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +37 -4
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +39 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +25 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +24 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +32 -5
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +41 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +39 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +43 -10
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +74 -59
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +143 -28
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +43 -12
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +135 -53
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +229 -36
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +27 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +162 -18
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +258 -51
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5987 -2620
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +25 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +154 -55
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +150 -35
- nautobot/project-static/docs/development/apps/api/configuration-view.html +23 -0
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +23 -0
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +23 -0
- nautobot/project-static/docs/development/apps/api/models/global-search.html +23 -0
- nautobot/project-static/docs/development/apps/api/models/graphql.html +23 -0
- nautobot/project-static/docs/development/apps/api/models/index.html +23 -0
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +23 -0
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +23 -0
- nautobot/project-static/docs/development/apps/api/prometheus.html +23 -0
- nautobot/project-static/docs/development/apps/api/setup.html +23 -0
- nautobot/project-static/docs/development/apps/api/testing.html +23 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +23 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +23 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +23 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +23 -0
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/base-template.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/index.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -2
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/notes.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +23 -0
- nautobot/project-static/docs/development/apps/api/views/urls.html +23 -0
- nautobot/project-static/docs/development/apps/index.html +23 -0
- nautobot/project-static/docs/development/apps/migration/code-updates.html +23 -0
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +23 -0
- nautobot/project-static/docs/development/apps/migration/from-v1.html +23 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +23 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +23 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +23 -0
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +23 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +26 -3
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +10544 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +23 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +23 -0
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +23 -0
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +26 -3
- nautobot/project-static/docs/development/core/application-registry.html +23 -0
- nautobot/project-static/docs/development/core/best-practices.html +23 -0
- nautobot/project-static/docs/development/core/bootstrap-ui.html +23 -0
- nautobot/project-static/docs/development/core/caching.html +23 -0
- nautobot/project-static/docs/development/core/controllers.html +23 -0
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +23 -0
- nautobot/project-static/docs/development/core/generic-views.html +23 -0
- nautobot/project-static/docs/development/core/getting-started.html +23 -0
- nautobot/project-static/docs/development/core/homepage.html +23 -0
- nautobot/project-static/docs/development/core/index.html +23 -0
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +23 -0
- nautobot/project-static/docs/development/core/model-checklist.html +23 -0
- nautobot/project-static/docs/development/core/model-features.html +23 -0
- nautobot/project-static/docs/development/core/natural-keys.html +23 -0
- nautobot/project-static/docs/development/core/navigation-menu.html +23 -0
- nautobot/project-static/docs/development/core/release-checklist.html +23 -0
- nautobot/project-static/docs/development/core/role-internals.html +23 -0
- nautobot/project-static/docs/development/core/settings.html +23 -0
- nautobot/project-static/docs/development/core/style-guide.html +23 -0
- nautobot/project-static/docs/development/core/templates.html +23 -0
- nautobot/project-static/docs/development/core/testing.html +23 -0
- nautobot/project-static/docs/development/core/ui-component-framework.html +713 -255
- nautobot/project-static/docs/development/core/user-preferences.html +23 -0
- nautobot/project-static/docs/development/index.html +23 -0
- nautobot/project-static/docs/development/jobs/getting-started.html +23 -0
- nautobot/project-static/docs/development/jobs/index.html +23 -0
- nautobot/project-static/docs/development/jobs/installation.html +23 -0
- nautobot/project-static/docs/development/jobs/job-extensions.html +23 -0
- nautobot/project-static/docs/development/jobs/job-logging.html +23 -0
- nautobot/project-static/docs/development/jobs/job-patterns.html +23 -0
- nautobot/project-static/docs/development/jobs/job-structure.html +23 -0
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -0
- nautobot/project-static/docs/development/jobs/testing.html +23 -0
- nautobot/project-static/docs/index.html +23 -0
- nautobot/project-static/docs/media/development/core/ui-component-framework/breadcrumbs-titles-data-flow.png +0 -0
- nautobot/project-static/docs/media/power_distribution.png +0 -0
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +23 -0
- nautobot/project-static/docs/overview/design_philosophy.html +23 -0
- nautobot/project-static/docs/release-notes/index.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.0.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.1.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.2.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.3.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.4.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.5.html +23 -0
- nautobot/project-static/docs/release-notes/version-1.6.html +23 -0
- nautobot/project-static/docs/release-notes/version-2.0.html +23 -0
- nautobot/project-static/docs/release-notes/version-2.1.html +23 -0
- nautobot/project-static/docs/release-notes/version-2.2.html +23 -0
- nautobot/project-static/docs/release-notes/version-2.3.html +23 -0
- nautobot/project-static/docs/release-notes/version-2.4.html +306 -0
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +303 -299
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +23 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +23 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +23 -0
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +23 -0
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +23 -0
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +23 -0
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +23 -0
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/index.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +23 -0
- nautobot/project-static/docs/user-guide/administration/installation/services.html +23 -0
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +23 -0
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +23 -0
- nautobot/project-static/docs/user-guide/administration/security/index.html +23 -0
- nautobot/project-static/docs/user-guide/administration/security/notices.html +23 -0
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +284 -219
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +23 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +305 -5
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +24 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +136 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +41 -1
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +40 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +23 -0
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +23 -0
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +23 -0
- nautobot/project-static/docs/user-guide/index.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +24 -1
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +23 -0
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +23 -0
- nautobot/users/tests/test_api.py +2 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -252
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -75
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +252 -0
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +75 -0
- nautobot/virtualization/urls.py +3 -61
- nautobot/virtualization/views.py +48 -72
- {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/METADATA +24 -24
- {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/RECORD +434 -417
- {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/NOTICE +0 -0
- {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/WHEEL +0 -0
- {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/entry_points.txt +0 -0
nautobot/dcim/models/power.py
CHANGED
|
@@ -6,7 +6,14 @@ from django.db import models
|
|
|
6
6
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
7
7
|
from nautobot.core.models.generics import PrimaryModel
|
|
8
8
|
from nautobot.core.models.validators import ExclusionValidator
|
|
9
|
-
from nautobot.dcim.choices import
|
|
9
|
+
from nautobot.dcim.choices import (
|
|
10
|
+
PowerFeedBreakerPoleChoices,
|
|
11
|
+
PowerFeedPhaseChoices,
|
|
12
|
+
PowerFeedSupplyChoices,
|
|
13
|
+
PowerFeedTypeChoices,
|
|
14
|
+
PowerPanelTypeChoices,
|
|
15
|
+
PowerPathChoices,
|
|
16
|
+
)
|
|
10
17
|
from nautobot.dcim.constants import (
|
|
11
18
|
POWERFEED_AMPERAGE_DEFAULT,
|
|
12
19
|
POWERFEED_MAX_UTILIZATION_DEFAULT,
|
|
@@ -46,6 +53,16 @@ class PowerPanel(PrimaryModel):
|
|
|
46
53
|
to="RackGroup", on_delete=models.PROTECT, blank=True, null=True, related_name="power_panels"
|
|
47
54
|
)
|
|
48
55
|
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, db_index=True)
|
|
56
|
+
panel_type = models.CharField(max_length=30, choices=PowerPanelTypeChoices, blank=True)
|
|
57
|
+
breaker_position_count = models.PositiveIntegerField(
|
|
58
|
+
null=True, blank=True, help_text="Total number of breaker positions in the panel (e.g., 42)"
|
|
59
|
+
)
|
|
60
|
+
power_path = models.CharField(
|
|
61
|
+
max_length=20,
|
|
62
|
+
choices=PowerPathChoices,
|
|
63
|
+
help_text="Physical power distribution redundancy path.",
|
|
64
|
+
blank=True,
|
|
65
|
+
)
|
|
49
66
|
|
|
50
67
|
natural_key_field_names = ["name", "location"]
|
|
51
68
|
|
|
@@ -98,7 +115,20 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
|
|
|
98
115
|
An electrical circuit delivered from a PowerPanel.
|
|
99
116
|
"""
|
|
100
117
|
|
|
101
|
-
power_panel = models.ForeignKey(
|
|
118
|
+
power_panel = models.ForeignKey(
|
|
119
|
+
to="PowerPanel",
|
|
120
|
+
on_delete=models.PROTECT,
|
|
121
|
+
related_name="power_feeds",
|
|
122
|
+
help_text="Source panel that originates this power feed",
|
|
123
|
+
)
|
|
124
|
+
destination_panel = models.ForeignKey(
|
|
125
|
+
to="PowerPanel",
|
|
126
|
+
on_delete=models.PROTECT,
|
|
127
|
+
blank=True,
|
|
128
|
+
null=True,
|
|
129
|
+
related_name="feeders",
|
|
130
|
+
help_text="Destination panel that receives power from this feed",
|
|
131
|
+
)
|
|
102
132
|
rack = models.ForeignKey(to="Rack", on_delete=models.PROTECT, blank=True, null=True, related_name="power_feeds")
|
|
103
133
|
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
|
|
104
134
|
status = StatusField(blank=False, null=False)
|
|
@@ -107,6 +137,12 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
|
|
|
107
137
|
choices=PowerFeedTypeChoices,
|
|
108
138
|
default=PowerFeedTypeChoices.TYPE_PRIMARY,
|
|
109
139
|
)
|
|
140
|
+
power_path = models.CharField(
|
|
141
|
+
max_length=20,
|
|
142
|
+
choices=PowerPathChoices,
|
|
143
|
+
help_text="Physical power distribution redundancy path.",
|
|
144
|
+
blank=True,
|
|
145
|
+
)
|
|
110
146
|
supply = models.CharField(
|
|
111
147
|
max_length=50,
|
|
112
148
|
choices=PowerFeedSupplyChoices,
|
|
@@ -124,11 +160,18 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
|
|
|
124
160
|
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
|
|
125
161
|
help_text="Maximum permissible draw (percentage)",
|
|
126
162
|
)
|
|
163
|
+
breaker_position = models.PositiveIntegerField(
|
|
164
|
+
null=True, blank=True, help_text="Starting circuit breaker position in panel"
|
|
165
|
+
)
|
|
166
|
+
breaker_pole_count = models.PositiveSmallIntegerField(
|
|
167
|
+
choices=PowerFeedBreakerPoleChoices, blank=True, null=True, help_text="Number of breaker poles"
|
|
168
|
+
)
|
|
127
169
|
available_power = models.PositiveIntegerField(default=0, editable=False)
|
|
128
170
|
comments = models.TextField(blank=True)
|
|
129
171
|
|
|
130
172
|
clone_fields = [
|
|
131
173
|
"power_panel",
|
|
174
|
+
"destination_panel",
|
|
132
175
|
"rack",
|
|
133
176
|
"status",
|
|
134
177
|
"type",
|
|
@@ -137,12 +180,19 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
|
|
|
137
180
|
"voltage",
|
|
138
181
|
"amperage",
|
|
139
182
|
"max_utilization",
|
|
183
|
+
"breaker_pole_count",
|
|
140
184
|
"available_power",
|
|
141
185
|
]
|
|
142
186
|
|
|
143
187
|
class Meta:
|
|
144
|
-
ordering = ["power_panel", "name"]
|
|
145
|
-
unique_together = [
|
|
188
|
+
ordering = ["power_panel", "breaker_position", "name"]
|
|
189
|
+
unique_together = [
|
|
190
|
+
["power_panel", "name"],
|
|
191
|
+
["power_panel", "breaker_position"],
|
|
192
|
+
]
|
|
193
|
+
indexes = [
|
|
194
|
+
models.Index(fields=["power_panel", "breaker_position"]),
|
|
195
|
+
]
|
|
146
196
|
|
|
147
197
|
def __str__(self):
|
|
148
198
|
return self.name
|
|
@@ -150,18 +200,87 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
|
|
|
150
200
|
def clean(self):
|
|
151
201
|
super().clean()
|
|
152
202
|
|
|
153
|
-
# Rack must belong to same
|
|
154
|
-
if self.rack and self.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
203
|
+
# Rack must belong to same location hierarchy as PowerPanel
|
|
204
|
+
if self.rack and self.power_panel.location: # pylint: disable=no-member
|
|
205
|
+
if self.rack.location not in self.power_panel.location.ancestors( # pylint: disable=no-member
|
|
206
|
+
include_self=True
|
|
207
|
+
) and self.power_panel.location not in self.rack.location.ancestors(include_self=True): # pylint: disable=no-member
|
|
208
|
+
raise ValidationError(
|
|
209
|
+
{
|
|
210
|
+
"rack": f'Rack "{self.rack}" ({self.rack.location}) and ' # pylint: disable=no-member
|
|
211
|
+
f'power panel "{self.power_panel}" ({self.power_panel.location}) ' # pylint: disable=no-member
|
|
212
|
+
f"are not in the same location hierarchy."
|
|
213
|
+
}
|
|
214
|
+
)
|
|
159
215
|
|
|
160
216
|
# AC voltage cannot be negative
|
|
161
217
|
if self.voltage < 0 and self.supply == PowerFeedSupplyChoices.SUPPLY_AC:
|
|
162
218
|
raise ValidationError({"voltage": "Voltage cannot be negative for AC supply"})
|
|
163
219
|
|
|
220
|
+
# Destination panel validation
|
|
221
|
+
if self.destination_panel:
|
|
222
|
+
# Cannot feed into the same panel
|
|
223
|
+
if self.destination_panel == self.power_panel:
|
|
224
|
+
raise ValidationError({"destination_panel": "A power feed cannot connect a panel to itself"})
|
|
225
|
+
# TODO: add loop detection when graph structure is implemented for path tracing
|
|
226
|
+
|
|
227
|
+
# Enforce mutual exclusivity between cable connections and destination_panel
|
|
228
|
+
if self.destination_panel and self.cable:
|
|
229
|
+
raise ValidationError(
|
|
230
|
+
{
|
|
231
|
+
"destination_panel": "Cannot specify a destination panel when the power feed is connected via cable. "
|
|
232
|
+
"Power feeds can either connect to a panel OR be cabled to an endpoint, but not both."
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Breaker position and pole validation
|
|
237
|
+
if self.breaker_position is not None:
|
|
238
|
+
# Default to single pole breaker when breaker_position is specified but breaker_pole_count is not
|
|
239
|
+
if self.breaker_pole_count is None:
|
|
240
|
+
self.breaker_pole_count = PowerFeedBreakerPoleChoices.POLE_1
|
|
241
|
+
|
|
242
|
+
# Get occupied positions once for both validations
|
|
243
|
+
occupied_positions = self.get_occupied_positions()
|
|
244
|
+
|
|
245
|
+
# Ensure breaker positions fit within panel capacity
|
|
246
|
+
if self.power_panel.breaker_position_count is not None:
|
|
247
|
+
if occupied_positions:
|
|
248
|
+
max_occupied_position = max(occupied_positions)
|
|
249
|
+
if max_occupied_position > self.power_panel.breaker_position_count:
|
|
250
|
+
raise ValidationError(
|
|
251
|
+
{
|
|
252
|
+
"breaker_position": f"Breaker configuration starting at position {self.breaker_position} "
|
|
253
|
+
f"with {self.breaker_pole_count} poles would occupy positions {sorted(occupied_positions)}, "
|
|
254
|
+
f"but panel only has {self.power_panel.breaker_position_count} breaker positions"
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Check for breaker position conflicts with other feeds
|
|
259
|
+
conflicts = PowerFeed.objects.filter(
|
|
260
|
+
power_panel=self.power_panel, breaker_position__isnull=False, breaker_pole_count__isnull=False
|
|
261
|
+
).exclude(pk=self.pk)
|
|
262
|
+
|
|
263
|
+
for feed in conflicts:
|
|
264
|
+
if occupied_positions.intersection(feed.get_occupied_positions()):
|
|
265
|
+
raise ValidationError(
|
|
266
|
+
{
|
|
267
|
+
"breaker_position": f"Breaker position {self.breaker_position} conflicts with "
|
|
268
|
+
f'feed "{feed.name}" (occupies {feed.occupied_positions})'
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
|
|
164
272
|
def save(self, *args, **kwargs):
|
|
273
|
+
# Enforce breaker pole count default
|
|
274
|
+
if self.breaker_position is not None and self.breaker_pole_count is None:
|
|
275
|
+
self.breaker_pole_count = PowerFeedBreakerPoleChoices.POLE_1
|
|
276
|
+
|
|
277
|
+
# Enforce mutual exclusivity between cable connections and destination_panel
|
|
278
|
+
if self.destination_panel and self.cable:
|
|
279
|
+
raise ValidationError(
|
|
280
|
+
"Cannot specify a destination panel when the power feed is connected via cable. "
|
|
281
|
+
"Power feeds can either connect to a panel OR be cabled to an endpoint, but not both."
|
|
282
|
+
)
|
|
283
|
+
|
|
165
284
|
# Cache the available_power property on the instance
|
|
166
285
|
kva = abs(self.voltage) * self.amperage * (self.max_utilization / 100)
|
|
167
286
|
if self.phase == PowerFeedPhaseChoices.PHASE_3PHASE:
|
|
@@ -175,5 +294,47 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination):
|
|
|
175
294
|
def parent(self):
|
|
176
295
|
return self.power_panel
|
|
177
296
|
|
|
297
|
+
@property
|
|
298
|
+
def occupied_positions(self) -> str:
|
|
299
|
+
"""All circuit positions occupied by this feed as comma-separated string."""
|
|
300
|
+
positions = self.get_occupied_positions()
|
|
301
|
+
return ", ".join(map(str, sorted(positions))) if positions else ""
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def phase_designation(self):
|
|
305
|
+
"""Calculate phase designation based on occupied circuit positions."""
|
|
306
|
+
if not (self.breaker_position and self.breaker_pole_count):
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
positions = self.get_occupied_positions()
|
|
310
|
+
if not positions:
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
# Positions 1,2=A, 3,4=B, 5,6=C, 7,8=A, 9,10=B, 11,12=C, etc.
|
|
314
|
+
def position_to_phase(pos):
|
|
315
|
+
# Calculate which phase this position is on
|
|
316
|
+
cycle_position = ((pos - 1) // 2) % 3
|
|
317
|
+
return ["A", "B", "C"][cycle_position]
|
|
318
|
+
|
|
319
|
+
phases = {position_to_phase(pos) for pos in positions}
|
|
320
|
+
|
|
321
|
+
# Return phase designation based on which phases are used
|
|
322
|
+
if len(phases) == 1:
|
|
323
|
+
return next(iter(phases)) # Single phase: "A", "B", or "C"
|
|
324
|
+
elif len(phases) == 2:
|
|
325
|
+
sorted_phases = sorted(phases)
|
|
326
|
+
return f"{sorted_phases[0]}-{sorted_phases[1]}" # "A-B", "B-C", etc.
|
|
327
|
+
elif len(phases) == 3:
|
|
328
|
+
return "A-B-C" # Three-phase
|
|
329
|
+
else:
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
def get_occupied_positions(self) -> set[int]:
|
|
333
|
+
"""Get set of circuit breaker positions occupied by this feed."""
|
|
334
|
+
if not (self.breaker_position and self.breaker_pole_count):
|
|
335
|
+
return set()
|
|
336
|
+
|
|
337
|
+
return set(self.breaker_position + (i * 2) for i in range(self.breaker_pole_count))
|
|
338
|
+
|
|
178
339
|
def get_type_class(self):
|
|
179
340
|
return PowerFeedTypeChoices.CSS_CLASSES.get(self.type)
|
nautobot/dcim/models/racks.py
CHANGED
|
@@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
4
4
|
from django.core.exceptions import ValidationError
|
|
5
5
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
6
6
|
from django.db import models
|
|
7
|
-
from django.db.models import Count, Q, Sum
|
|
7
|
+
from django.db.models import Count, F, Q, Sum
|
|
8
8
|
|
|
9
9
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
10
10
|
from nautobot.core.models.fields import JSONArrayField, NaturalOrderingField
|
|
@@ -426,6 +426,7 @@ class Rack(PrimaryModel):
|
|
|
426
426
|
Returns:
|
|
427
427
|
UtilizationData: (numerator, denominator)
|
|
428
428
|
"""
|
|
429
|
+
|
|
429
430
|
powerfeeds = PowerFeed.objects.filter(rack=self)
|
|
430
431
|
available_power_total = sum(pf.available_power for pf in powerfeeds)
|
|
431
432
|
if not available_power_total:
|
|
@@ -435,13 +436,15 @@ class Rack(PrimaryModel):
|
|
|
435
436
|
_cable_peer_type=ContentType.objects.get_for_model(PowerFeed),
|
|
436
437
|
_cable_peer_id__in=powerfeeds.values_list("id", flat=True),
|
|
437
438
|
)
|
|
438
|
-
direct_allocated_draw =
|
|
439
|
+
direct_allocated_draw = int(
|
|
440
|
+
pf_powerports.aggregate(total=Sum(F("allocated_draw") / F("power_factor")))["total"] or 0
|
|
441
|
+
)
|
|
439
442
|
poweroutlets = PowerOutlet.objects.filter(power_port_id__in=pf_powerports)
|
|
440
|
-
allocated_draw_total = (
|
|
443
|
+
allocated_draw_total = int(
|
|
441
444
|
PowerPort.objects.filter(
|
|
442
445
|
_cable_peer_type=ContentType.objects.get_for_model(PowerOutlet),
|
|
443
446
|
_cable_peer_id__in=poweroutlets.values_list("id", flat=True),
|
|
444
|
-
).aggregate(Sum("allocated_draw"))["
|
|
447
|
+
).aggregate(total=Sum(F("allocated_draw") / F("power_factor")))["total"]
|
|
445
448
|
or 0
|
|
446
449
|
)
|
|
447
450
|
allocated_draw_total += direct_allocated_draw
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -534,6 +534,7 @@ class PowerPortTable(ModularDeviceComponentTable, PathEndpointTable):
|
|
|
534
534
|
"description",
|
|
535
535
|
"maximum_draw",
|
|
536
536
|
"allocated_draw",
|
|
537
|
+
"power_factor",
|
|
537
538
|
"cable",
|
|
538
539
|
"cable_peer",
|
|
539
540
|
"connection",
|
|
@@ -570,6 +571,7 @@ class DeviceModulePowerPortTable(PowerPortTable):
|
|
|
570
571
|
"type",
|
|
571
572
|
"maximum_draw",
|
|
572
573
|
"allocated_draw",
|
|
574
|
+
"power_factor",
|
|
573
575
|
"description",
|
|
574
576
|
"cable",
|
|
575
577
|
"cable_peer",
|
nautobot/dcim/tables/power.py
CHANGED
|
@@ -27,6 +27,9 @@ class PowerPanelTable(BaseTable):
|
|
|
27
27
|
pk = ToggleColumn()
|
|
28
28
|
name = tables.LinkColumn()
|
|
29
29
|
location = tables.Column(linkify=True)
|
|
30
|
+
panel_type = tables.Column()
|
|
31
|
+
power_path = tables.Column()
|
|
32
|
+
breaker_position_count = tables.Column(verbose_name="Total Breaker Positions")
|
|
30
33
|
power_feed_count = LinkedCountColumn(
|
|
31
34
|
viewname="dcim:powerfeed_list",
|
|
32
35
|
url_params={"power_panel": "pk"},
|
|
@@ -36,8 +39,26 @@ class PowerPanelTable(BaseTable):
|
|
|
36
39
|
|
|
37
40
|
class Meta(BaseTable.Meta):
|
|
38
41
|
model = PowerPanel
|
|
39
|
-
fields = (
|
|
40
|
-
|
|
42
|
+
fields = (
|
|
43
|
+
"pk",
|
|
44
|
+
"name",
|
|
45
|
+
"location",
|
|
46
|
+
"rack_group",
|
|
47
|
+
"panel_type",
|
|
48
|
+
"power_path",
|
|
49
|
+
"breaker_position_count",
|
|
50
|
+
"power_feed_count",
|
|
51
|
+
"tags",
|
|
52
|
+
)
|
|
53
|
+
default_columns = (
|
|
54
|
+
"pk",
|
|
55
|
+
"name",
|
|
56
|
+
"location",
|
|
57
|
+
"rack_group",
|
|
58
|
+
"panel_type",
|
|
59
|
+
"power_path",
|
|
60
|
+
"power_feed_count",
|
|
61
|
+
)
|
|
41
62
|
|
|
42
63
|
|
|
43
64
|
#
|
|
@@ -51,8 +72,12 @@ class PowerFeedTable(StatusTableMixin, CableTerminationTable):
|
|
|
51
72
|
pk = ToggleColumn()
|
|
52
73
|
name = tables.LinkColumn()
|
|
53
74
|
power_panel = tables.Column(linkify=True)
|
|
75
|
+
destination_panel = tables.Column(linkify=True)
|
|
54
76
|
rack = tables.Column(linkify=True)
|
|
55
77
|
type = ChoiceFieldColumn()
|
|
78
|
+
power_path = tables.Column()
|
|
79
|
+
occupied_positions = tables.Column(accessor="occupied_positions", verbose_name="Position")
|
|
80
|
+
phase_designation = tables.Column(accessor="phase_designation", verbose_name="Phase Designation")
|
|
56
81
|
max_utilization = tables.TemplateColumn(template_code="{{ value }}%")
|
|
57
82
|
available_power = tables.Column(verbose_name="Available power (VA)")
|
|
58
83
|
tags = TagColumn(url_name="dcim:powerfeed_list")
|
|
@@ -63,9 +88,11 @@ class PowerFeedTable(StatusTableMixin, CableTerminationTable):
|
|
|
63
88
|
"pk",
|
|
64
89
|
"name",
|
|
65
90
|
"power_panel",
|
|
91
|
+
"destination_panel",
|
|
66
92
|
"rack",
|
|
67
93
|
"status",
|
|
68
94
|
"type",
|
|
95
|
+
"power_path",
|
|
69
96
|
"supply",
|
|
70
97
|
"voltage",
|
|
71
98
|
"amperage",
|
|
@@ -84,6 +111,7 @@ class PowerFeedTable(StatusTableMixin, CableTerminationTable):
|
|
|
84
111
|
"rack",
|
|
85
112
|
"status",
|
|
86
113
|
"type",
|
|
114
|
+
"power_path",
|
|
87
115
|
"supply",
|
|
88
116
|
"voltage",
|
|
89
117
|
"amperage",
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
{% plugin_left_page object %}
|
|
243
243
|
</div>
|
|
244
244
|
<div class="col-md-6">
|
|
245
|
-
{% if object.
|
|
245
|
+
{% if object.power_ports.exists and object.power_outlets.exists %}
|
|
246
246
|
<div class="panel panel-default">
|
|
247
247
|
<div class="panel-heading">
|
|
248
248
|
<strong>Power Utilization</strong>
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
<th>Available</th>
|
|
256
256
|
<th>Utilization</th>
|
|
257
257
|
</tr>
|
|
258
|
-
{% for powerport in object.
|
|
258
|
+
{% for powerport in object.power_ports.all %}
|
|
259
259
|
{% with utilization=powerport.get_power_draw powerfeed=powerport.connected_endpoint %}
|
|
260
260
|
<tr>
|
|
261
261
|
<td>{{ powerport }}</td>
|
|
@@ -1,217 +1,4 @@
|
|
|
1
1
|
{% extends 'generic/object_retrieve.html' %}
|
|
2
|
-
{% load buttons %}
|
|
3
|
-
{% load helpers %}
|
|
4
|
-
{% load plugins %}
|
|
5
|
-
|
|
6
2
|
{% block extra_breadcrumbs %}
|
|
7
3
|
<li><a href="{% url 'dcim:devicetype_list' %}?manufacturer={{ object.manufacturer.name }}">{{ object.manufacturer }}</a></li>
|
|
8
|
-
{% endblock extra_breadcrumbs %}
|
|
9
|
-
|
|
10
|
-
{% block extra_buttons %}
|
|
11
|
-
{% if perms.dcim.change_devicetype %}
|
|
12
|
-
<div class="btn-group">
|
|
13
|
-
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="device-type-add-components-button">
|
|
14
|
-
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
|
15
|
-
</button>
|
|
16
|
-
<ul class="dropdown-menu">
|
|
17
|
-
{% if perms.dcim.add_consoleporttemplate %}
|
|
18
|
-
<li><a href="{% url 'dcim:consoleporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=consoleports">Console Ports</a></li>
|
|
19
|
-
{% endif %}
|
|
20
|
-
{% if perms.dcim.add_consoleserverporttemplate %}
|
|
21
|
-
<li><a href="{% url 'dcim:consoleserverporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=consoleserverports">Console Server Ports</a></li>
|
|
22
|
-
{% endif %}
|
|
23
|
-
{% if perms.dcim.add_powerporttemplate %}
|
|
24
|
-
<li><a href="{% url 'dcim:powerporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=powerports">Power Ports</a></li>
|
|
25
|
-
{% endif %}
|
|
26
|
-
{% if perms.dcim.add_poweroutlettemplate %}
|
|
27
|
-
<li><a href="{% url 'dcim:poweroutlettemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=poweroutlets">Power Outlets</a></li>
|
|
28
|
-
{% endif %}
|
|
29
|
-
{% if perms.dcim.add_interfacetemplate %}
|
|
30
|
-
<li><a href="{% url 'dcim:interfacetemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=interfaces">Interfaces</a></li>
|
|
31
|
-
{% endif %}
|
|
32
|
-
{% if perms.dcim.add_frontporttemplate %}
|
|
33
|
-
<li><a href="{% url 'dcim:frontporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=frontports">Front Ports</a></li>
|
|
34
|
-
{% endif %}
|
|
35
|
-
{% if perms.dcim.add_rearporttemplate %}
|
|
36
|
-
<li><a href="{% url 'dcim:rearporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=rearports">Rear Ports</a></li>
|
|
37
|
-
{% endif %}
|
|
38
|
-
{% if perms.dcim.add_devicebaytemplate %}
|
|
39
|
-
<li><a href="{% url 'dcim:devicebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=devicebays">Device Bays</a></li>
|
|
40
|
-
{% endif %}
|
|
41
|
-
{% if perms.dcim.add_modulebaytemplate %}
|
|
42
|
-
<li><a href="{% url 'dcim:modulebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%3Ftab=modulebays">Module Bays</a></li>
|
|
43
|
-
{% endif %}
|
|
44
|
-
</ul>
|
|
45
|
-
</div>
|
|
46
|
-
{% endif %}
|
|
47
|
-
{% endblock extra_buttons %}
|
|
48
|
-
|
|
49
|
-
{% block title %}{{ object.manufacturer }} {{ object.model }}{% endblock title %}
|
|
50
|
-
|
|
51
|
-
{% block extra_nav_tabs %}
|
|
52
|
-
<li role="presentation" {% if request.GET.tab == 'interfaces' %} class="active"{% endif %}>
|
|
53
|
-
<a href="{{ object.get_absolute_url }}#interfaces" onclick="switch_tab(this.href)" aria-controls="interfaces" role="tab" data-toggle="tab">
|
|
54
|
-
Interfaces {% badge interface_table.rows|length %}
|
|
55
|
-
</a>
|
|
56
|
-
</li>
|
|
57
|
-
<li role="presentation" {% if request.GET.tab == 'frontports' %} class="active"{% endif %}>
|
|
58
|
-
<a href="{{ object.get_absolute_url }}#frontports" onclick="switch_tab(this.href)" aria-controls="frontports" role="tab" data-toggle="tab">
|
|
59
|
-
Front Ports {% badge front_port_table.rows|length %}
|
|
60
|
-
</a>
|
|
61
|
-
</li>
|
|
62
|
-
<li role="presentation" {% if request.GET.tab == 'rearports' %} class="active"{% endif %}>
|
|
63
|
-
<a href="{{ object.get_absolute_url }}#rearports" onclick="switch_tab(this.href)" aria-controls="rearports" role="tab" data-toggle="tab">
|
|
64
|
-
Rear Ports {% badge rear_port_table.rows|length %}
|
|
65
|
-
</a>
|
|
66
|
-
</li>
|
|
67
|
-
<li role="presentation" {% if request.GET.tab == 'consoleports' %} class="active"{% endif %}>
|
|
68
|
-
<a href="{{ object.get_absolute_url }}#consoleports" onclick="switch_tab(this.href)" aria-controls="consoleports" role="tab" data-toggle="tab">
|
|
69
|
-
Console Ports {% badge consoleport_table.rows|length %}
|
|
70
|
-
</a>
|
|
71
|
-
</li>
|
|
72
|
-
<li role="presentation" {% if request.GET.tab == 'consoleserverports' %} class="active"{% endif %}>
|
|
73
|
-
<a href="{{ object.get_absolute_url }}#consoleserverports" onclick="switch_tab(this.href)" aria-controls="consoleserverports" role="tab" data-toggle="tab">
|
|
74
|
-
Console Server Ports {% badge consoleserverport_table.rows|length %}
|
|
75
|
-
</a>
|
|
76
|
-
</li>
|
|
77
|
-
<li role="presentation" {% if request.GET.tab == 'powerports' %} class="active"{% endif %}>
|
|
78
|
-
<a href="{{ object.get_absolute_url }}#powerports" onclick="switch_tab(this.href)" aria-controls="powerports" role="tab" data-toggle="tab">
|
|
79
|
-
Power Ports {% badge powerport_table.rows|length %}
|
|
80
|
-
</a>
|
|
81
|
-
</li>
|
|
82
|
-
<li role="presentation" {% if request.GET.tab == 'poweroutlets' %} class="active"{% endif %}>
|
|
83
|
-
<a href="{{ object.get_absolute_url }}#poweroutlets" onclick="switch_tab(this.href)" aria-controls="poweroutlets" role="tab" data-toggle="tab">
|
|
84
|
-
Power Outlets {% badge poweroutlet_table.rows|length %}
|
|
85
|
-
</a>
|
|
86
|
-
</li>
|
|
87
|
-
<li role="presentation"{% if request.GET.tab == 'devicebays' %} class="active"{% endif %}>
|
|
88
|
-
<a href="{{ object.get_absolute_url }}#devicebays" onclick="switch_tab(this.href)" aria-controls="devicebays" role="tab" data-toggle="tab">
|
|
89
|
-
Device Bays {% badge devicebay_table.rows|length %}
|
|
90
|
-
</a>
|
|
91
|
-
</li>
|
|
92
|
-
<li role="presentation"{% if request.GET.tab == 'modulebays' %} class="active"{% endif %}>
|
|
93
|
-
<a href="{{ object.get_absolute_url }}#modulebays" onclick="switch_tab(this.href)" aria-controls="modulebays" role="tab" data-toggle="tab">
|
|
94
|
-
Module Bays {% badge modulebay_table.rows|length %}
|
|
95
|
-
</a>
|
|
96
|
-
</li>
|
|
97
|
-
{% endblock extra_nav_tabs %}
|
|
98
|
-
|
|
99
|
-
{% block content_left_page %}
|
|
100
|
-
<div class="panel panel-default">
|
|
101
|
-
<div class="panel-heading">
|
|
102
|
-
<strong>Chassis</strong>
|
|
103
|
-
</div>
|
|
104
|
-
<table class="table table-hover panel-body attr-table">
|
|
105
|
-
<tr>
|
|
106
|
-
<td>Manufacturer</td>
|
|
107
|
-
<td><a href="{% url 'dcim:devicetype_list' %}?manufacturer={{ object.manufacturer.name }}">{{ object.manufacturer }}</a></td>
|
|
108
|
-
</tr>
|
|
109
|
-
<tr>
|
|
110
|
-
<td>Device Family</td>
|
|
111
|
-
<td>
|
|
112
|
-
{{ object.device_family | hyperlinked_object }}
|
|
113
|
-
</td>
|
|
114
|
-
</tr>
|
|
115
|
-
<tr>
|
|
116
|
-
<td>Model Name</td>
|
|
117
|
-
<td>
|
|
118
|
-
{{ object.model }}<br/>
|
|
119
|
-
</td>
|
|
120
|
-
</tr>
|
|
121
|
-
<tr>
|
|
122
|
-
<td>Part Number</td>
|
|
123
|
-
<td>{{ object.part_number|placeholder }}</td>
|
|
124
|
-
</tr>
|
|
125
|
-
<tr>
|
|
126
|
-
<td>Height (U)</td>
|
|
127
|
-
<td>{{ object.u_height }}</td>
|
|
128
|
-
</tr>
|
|
129
|
-
<tr>
|
|
130
|
-
<td>Full Depth</td>
|
|
131
|
-
<td>{{ object.is_full_depth | render_boolean }}</td>
|
|
132
|
-
</tr>
|
|
133
|
-
<tr>
|
|
134
|
-
<td>Parent/Child</td>
|
|
135
|
-
<td>
|
|
136
|
-
{{ object.get_subdevice_role_display|placeholder }}
|
|
137
|
-
</td>
|
|
138
|
-
</tr>
|
|
139
|
-
<tr>
|
|
140
|
-
<td>Front Image</td>
|
|
141
|
-
<td>
|
|
142
|
-
{% if object.front_image %}
|
|
143
|
-
<a href="{{ object.front_image.url }}">
|
|
144
|
-
<img src="{{ object.front_image.url }}" alt="{{ object.front_image.name }}" class="img-responsive" />
|
|
145
|
-
</a>
|
|
146
|
-
{% else %}
|
|
147
|
-
<span class="text-muted">—</span>
|
|
148
|
-
{% endif %}
|
|
149
|
-
</td>
|
|
150
|
-
</tr>
|
|
151
|
-
<tr>
|
|
152
|
-
<td>Rear Image</td>
|
|
153
|
-
<td>
|
|
154
|
-
{% if object.rear_image %}
|
|
155
|
-
<a href="{{ object.rear_image.url }}">
|
|
156
|
-
<img src="{{ object.rear_image.url }}" alt="{{ object.rear_image.name }}" class="img-responsive" />
|
|
157
|
-
</a>
|
|
158
|
-
{% else %}
|
|
159
|
-
<span class="text-muted">—</span>
|
|
160
|
-
{% endif %}
|
|
161
|
-
</td>
|
|
162
|
-
</tr>
|
|
163
|
-
<tr>
|
|
164
|
-
<td>Device Instances</td>
|
|
165
|
-
<td><a href="{% url 'dcim:device_list' %}?device_type={{ object.pk }}">{{ instance_count }}</a></td>
|
|
166
|
-
</tr>
|
|
167
|
-
</table>
|
|
168
|
-
</div>
|
|
169
|
-
{% endblock content_left_page %}
|
|
170
|
-
|
|
171
|
-
{% block content_right_page %}
|
|
172
|
-
<div class="panel panel-default">
|
|
173
|
-
<div class="panel-heading">
|
|
174
|
-
<strong>Comments</strong>
|
|
175
|
-
</div>
|
|
176
|
-
<div class="panel-body rendered-markdown">
|
|
177
|
-
{% if object.comments %}
|
|
178
|
-
{{ object.comments|render_markdown }}
|
|
179
|
-
{% else %}
|
|
180
|
-
<span class="text-muted">None</span>
|
|
181
|
-
{% endif %}
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
{% if object.software_image_files.count %}
|
|
185
|
-
{% include 'panel_table.html' with table=software_image_files_table heading='Software Image Files' %}
|
|
186
|
-
{% endif %}
|
|
187
|
-
{% endblock content_right_page %}
|
|
188
|
-
|
|
189
|
-
{% block extra_tab_content %}
|
|
190
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'interfaces' %}active{% else %}fade{% endif %}" id="interfaces">
|
|
191
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' tab='interfaces' %}
|
|
192
|
-
</div>
|
|
193
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'frontports' %}active{% else %}fade{% endif %}" id="frontports">
|
|
194
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=front_port_table title='Front Ports' tab='frontports' %}
|
|
195
|
-
</div>
|
|
196
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'rearports' %}active{% else %}fade{% endif %}" id="rearports">
|
|
197
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=rear_port_table title='Rear Ports' tab='rearports' %}
|
|
198
|
-
</div>
|
|
199
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'consoleports' %}active{% else %}fade{% endif %}" id="consoleports">
|
|
200
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' tab='consoleports' %}
|
|
201
|
-
</div>
|
|
202
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'consoleserverports' %}active{% else %}fade{% endif %}" id="consoleserverports">
|
|
203
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' tab='consoleserverports' %}
|
|
204
|
-
</div>
|
|
205
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'powerports' %}active{% else %}fade{% endif %}" id="powerports">
|
|
206
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' tab='powerports' %}
|
|
207
|
-
</div>
|
|
208
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'poweroutlets' %}active{% else %}fade{% endif %}" id="poweroutlets">
|
|
209
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' tab='poweroutlets' %}
|
|
210
|
-
</div>
|
|
211
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'devicebays' %}active{% else %}fade{% endif %}" id="devicebays">
|
|
212
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' tab='devicebays' %}
|
|
213
|
-
</div>
|
|
214
|
-
<div role="tabpanel" class="tab-pane {% if request.GET.tab == 'modulebays' %}active{% else %}fade{% endif %}" id="modulebays">
|
|
215
|
-
{% include 'dcim/inc/devicetype_component_table.html' with table=modulebay_table title='Module Bays' tab='modulebays' %}
|
|
216
|
-
</div>
|
|
217
|
-
{% endblock extra_tab_content %}
|
|
4
|
+
{% endblock extra_breadcrumbs %}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<tr>
|
|
60
60
|
<td>Children</td>
|
|
61
61
|
<td>
|
|
62
|
-
{% if object.
|
|
62
|
+
{% if object.children.all %}
|
|
63
63
|
<a href="{% url 'dcim:location_list' %}?parent={{ object.pk }}">{{ children_table.rows|length }}</a>
|
|
64
64
|
{% else %}
|
|
65
65
|
{{ None|placeholder }}
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
{% endblock content_right_page %}
|
|
231
231
|
|
|
232
232
|
{% block content_full_width_page %}
|
|
233
|
-
{% if object.
|
|
233
|
+
{% if object.children.all %}
|
|
234
234
|
<div class="panel panel-default">
|
|
235
235
|
<div class="panel-heading">
|
|
236
236
|
<strong>Children</strong>
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<div class="panel-body">
|
|
8
8
|
{% render_field form.location %}
|
|
9
9
|
{% render_field form.power_panel %}
|
|
10
|
+
{% render_field form.destination_panel %}
|
|
10
11
|
</div>
|
|
11
12
|
</div>
|
|
12
13
|
<div class="panel panel-default">
|
|
@@ -28,6 +29,13 @@
|
|
|
28
29
|
{% render_field form.max_utilization %}
|
|
29
30
|
</div>
|
|
30
31
|
</div>
|
|
32
|
+
<div class="panel panel-default">
|
|
33
|
+
<div class="panel-heading"><strong>Circuit Breaker Configuration</strong></div>
|
|
34
|
+
<div class="panel-body">
|
|
35
|
+
{% render_field form.breaker_position %}
|
|
36
|
+
{% render_field form.breaker_pole_count %}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
31
39
|
{% include 'inc/extras_features_edit_form_fields.html' %}
|
|
32
40
|
<div class="panel panel-default">
|
|
33
41
|
<div class="panel-heading"><strong>Comments</strong></div>
|