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.
Files changed (434) hide show
  1. nautobot/apps/choices.py +8 -0
  2. nautobot/apps/ui.py +14 -0
  3. nautobot/core/api/views.py +2 -0
  4. nautobot/core/choices.py +4 -0
  5. nautobot/core/filters.py +21 -41
  6. nautobot/core/management/commands/check_job_approval_status.py +47 -0
  7. nautobot/core/management/commands/generate_test_data.py +1 -1
  8. nautobot/core/management/commands/migrate.py +1 -1
  9. nautobot/core/models/tree_queries.py +17 -0
  10. nautobot/core/settings.py +2 -2
  11. nautobot/core/tables.py +25 -2
  12. nautobot/core/templates/base_django.html +1 -1
  13. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  14. nautobot/core/templates/generic/object_list.html +17 -20
  15. nautobot/core/templates/inc/breadcrumbs.html +14 -0
  16. nautobot/core/templatetags/buttons.py +2 -4
  17. nautobot/core/templatetags/helpers.py +29 -6
  18. nautobot/core/templatetags/ui_framework.py +21 -0
  19. nautobot/core/testing/filters.py +20 -3
  20. nautobot/core/testing/forms.py +1 -1
  21. nautobot/core/tests/integration/test_filters.py +2 -2
  22. nautobot/core/tests/test_breadcrumbs.py +366 -0
  23. nautobot/core/tests/test_commands.py +40 -0
  24. nautobot/core/tests/test_filters.py +51 -1
  25. nautobot/core/tests/test_forms.py +1 -1
  26. nautobot/core/tests/test_graphql.py +4 -4
  27. nautobot/core/tests/test_titles.py +183 -0
  28. nautobot/core/tests/test_tree_queries.py +30 -0
  29. nautobot/core/tests/test_views.py +2 -2
  30. nautobot/core/tests/test_views_utils.py +1 -1
  31. nautobot/core/ui/breadcrumbs.py +538 -0
  32. nautobot/core/ui/bulk_buttons.py +53 -0
  33. nautobot/core/ui/object_detail.py +31 -8
  34. nautobot/core/ui/titles.py +127 -0
  35. nautobot/core/ui/utils.py +25 -0
  36. nautobot/core/utils/migrations.py +1 -1
  37. nautobot/core/views/__init__.py +1 -1
  38. nautobot/core/views/mixins.py +26 -1
  39. nautobot/core/views/renderers.py +20 -2
  40. nautobot/core/views/utils.py +13 -12
  41. nautobot/dcim/api/serializers.py +9 -0
  42. nautobot/dcim/choices.py +53 -0
  43. nautobot/dcim/filters/__init__.py +15 -3
  44. nautobot/dcim/forms.py +120 -7
  45. nautobot/dcim/management/commands/trace_paths.py +1 -1
  46. nautobot/dcim/migrations/0072_alter_powerfeed_options_and_more.py +97 -0
  47. nautobot/dcim/models/device_component_templates.py +8 -0
  48. nautobot/dcim/models/device_components.py +31 -12
  49. nautobot/dcim/models/devices.py +1 -1
  50. nautobot/dcim/models/power.py +171 -10
  51. nautobot/dcim/models/racks.py +7 -4
  52. nautobot/dcim/tables/devices.py +2 -0
  53. nautobot/dcim/tables/devicetypes.py +1 -0
  54. nautobot/dcim/tables/power.py +30 -2
  55. nautobot/dcim/templates/dcim/device.html +2 -2
  56. nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -214
  57. nautobot/dcim/templates/dcim/location_retrieve.html +2 -2
  58. nautobot/dcim/templates/dcim/powerfeed_edit.html +8 -0
  59. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -1
  60. nautobot/dcim/tests/integration/test_device_bulk_operations.py +61 -0
  61. nautobot/dcim/tests/test_api.py +24 -4
  62. nautobot/dcim/tests/test_filters.py +91 -13
  63. nautobot/dcim/tests/test_models.py +262 -0
  64. nautobot/dcim/tests/test_views.py +20 -12
  65. nautobot/dcim/utils.py +9 -0
  66. nautobot/dcim/views.py +390 -77
  67. nautobot/extras/factory.py +19 -20
  68. nautobot/extras/filters/__init__.py +3 -2
  69. nautobot/extras/filters/mixins.py +15 -1
  70. nautobot/extras/forms/__init__.py +2 -1
  71. nautobot/extras/forms/forms.py +62 -0
  72. nautobot/extras/managers.py +4 -1
  73. nautobot/extras/migrations/0125_jobresult_date_started.py +18 -0
  74. nautobot/extras/models/customfields.py +1 -2
  75. nautobot/extras/models/datasources.py +1 -2
  76. nautobot/extras/models/jobs.py +7 -3
  77. nautobot/extras/plugins/views.py +24 -1
  78. nautobot/extras/secrets/__init__.py +1 -1
  79. nautobot/extras/tables.py +21 -0
  80. nautobot/extras/templates/extras/customfield.html +2 -129
  81. nautobot/extras/templates/extras/customfield_edit.html +2 -108
  82. nautobot/extras/templates/extras/customfield_retrieve.html +129 -0
  83. nautobot/extras/templates/extras/customfield_update.html +108 -0
  84. nautobot/extras/templates/extras/inc/jobresult.html +7 -3
  85. nautobot/extras/templates/extras/jobresult.html +2 -155
  86. nautobot/extras/templates/extras/jobresult_retrieve.html +155 -0
  87. nautobot/extras/templates/extras/marketplace.html +5 -6
  88. nautobot/extras/templates/extras/note.html +2 -53
  89. nautobot/extras/templates/extras/note_retrieve.html +53 -0
  90. nautobot/extras/templates/extras/plugins_list.html +5 -6
  91. nautobot/extras/templates/extras/secretsgroup_retrieve.html +2 -29
  92. nautobot/extras/templatetags/custom_links.py +2 -2
  93. nautobot/extras/templatetags/job_buttons.py +1 -1
  94. nautobot/extras/templatetags/plugins.py +1 -1
  95. nautobot/extras/tests/integration/test_computedfields.py +2 -2
  96. nautobot/extras/tests/integration/test_customfields.py +14 -11
  97. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  98. nautobot/extras/tests/integration/test_notes.py +1 -1
  99. nautobot/extras/tests/integration/test_plugins.py +6 -6
  100. nautobot/extras/tests/integration/test_relationships.py +2 -2
  101. nautobot/extras/tests/test_filters.py +9 -0
  102. nautobot/extras/tests/test_forms.py +2 -2
  103. nautobot/extras/tests/test_plugins.py +14 -3
  104. nautobot/extras/tests/test_relationships.py +7 -7
  105. nautobot/extras/tests/test_views.py +172 -1
  106. nautobot/extras/urls.py +3 -59
  107. nautobot/extras/utils.py +1 -1
  108. nautobot/extras/views.py +96 -182
  109. nautobot/ipam/tables.py +8 -15
  110. nautobot/ipam/tests/migration/test_migrations.py +8 -8
  111. nautobot/ipam/tests/test_api.py +2 -2
  112. nautobot/ipam/tests/test_models.py +1 -1
  113. nautobot/project-static/docs/404.html +23 -0
  114. nautobot/project-static/docs/apps/index.html +23 -0
  115. nautobot/project-static/docs/apps/nautobot-apps.html +23 -0
  116. nautobot/project-static/docs/assets/_mkdocstrings.css +44 -6
  117. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +28 -0
  118. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +25 -0
  119. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +128 -20
  120. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +37 -4
  121. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +39 -6
  122. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +25 -0
  123. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +24 -0
  124. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +32 -5
  125. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +41 -8
  126. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +39 -7
  127. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +43 -10
  128. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +74 -59
  129. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +143 -28
  130. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +43 -12
  131. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +135 -53
  132. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +229 -36
  133. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +27 -1
  134. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -1
  135. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +162 -18
  136. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +258 -51
  137. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5987 -2620
  138. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +25 -0
  139. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +154 -55
  140. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +150 -35
  141. nautobot/project-static/docs/development/apps/api/configuration-view.html +23 -0
  142. nautobot/project-static/docs/development/apps/api/database-backend-config.html +23 -0
  143. nautobot/project-static/docs/development/apps/api/models/django-admin.html +23 -0
  144. nautobot/project-static/docs/development/apps/api/models/global-search.html +23 -0
  145. nautobot/project-static/docs/development/apps/api/models/graphql.html +23 -0
  146. nautobot/project-static/docs/development/apps/api/models/index.html +23 -0
  147. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +23 -0
  148. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +23 -0
  149. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +23 -0
  150. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +23 -0
  151. nautobot/project-static/docs/development/apps/api/platform-features/index.html +23 -0
  152. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +23 -0
  153. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +23 -0
  154. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +23 -0
  155. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +23 -0
  156. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +23 -0
  157. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +23 -0
  158. nautobot/project-static/docs/development/apps/api/prometheus.html +23 -0
  159. nautobot/project-static/docs/development/apps/api/setup.html +23 -0
  160. nautobot/project-static/docs/development/apps/api/testing.html +23 -0
  161. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +23 -0
  162. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +23 -0
  163. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +23 -0
  164. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +23 -0
  165. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +23 -0
  166. nautobot/project-static/docs/development/apps/api/views/base-template.html +23 -0
  167. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +23 -0
  168. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +23 -0
  169. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +23 -0
  170. nautobot/project-static/docs/development/apps/api/views/index.html +23 -0
  171. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +23 -0
  172. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -2
  173. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +23 -0
  174. nautobot/project-static/docs/development/apps/api/views/notes.html +23 -0
  175. nautobot/project-static/docs/development/apps/api/views/rest-api.html +23 -0
  176. nautobot/project-static/docs/development/apps/api/views/urls.html +23 -0
  177. nautobot/project-static/docs/development/apps/index.html +23 -0
  178. nautobot/project-static/docs/development/apps/migration/code-updates.html +23 -0
  179. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +23 -0
  180. nautobot/project-static/docs/development/apps/migration/from-v1.html +23 -0
  181. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +23 -0
  182. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +23 -0
  183. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +23 -0
  184. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +23 -0
  185. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +26 -3
  186. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +10544 -0
  187. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +23 -0
  188. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +23 -0
  189. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +23 -0
  190. nautobot/project-static/docs/development/apps/porting-from-netbox.html +26 -3
  191. nautobot/project-static/docs/development/core/application-registry.html +23 -0
  192. nautobot/project-static/docs/development/core/best-practices.html +23 -0
  193. nautobot/project-static/docs/development/core/bootstrap-ui.html +23 -0
  194. nautobot/project-static/docs/development/core/caching.html +23 -0
  195. nautobot/project-static/docs/development/core/controllers.html +23 -0
  196. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +23 -0
  197. nautobot/project-static/docs/development/core/generic-views.html +23 -0
  198. nautobot/project-static/docs/development/core/getting-started.html +23 -0
  199. nautobot/project-static/docs/development/core/homepage.html +23 -0
  200. nautobot/project-static/docs/development/core/index.html +23 -0
  201. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +23 -0
  202. nautobot/project-static/docs/development/core/model-checklist.html +23 -0
  203. nautobot/project-static/docs/development/core/model-features.html +23 -0
  204. nautobot/project-static/docs/development/core/natural-keys.html +23 -0
  205. nautobot/project-static/docs/development/core/navigation-menu.html +23 -0
  206. nautobot/project-static/docs/development/core/release-checklist.html +23 -0
  207. nautobot/project-static/docs/development/core/role-internals.html +23 -0
  208. nautobot/project-static/docs/development/core/settings.html +23 -0
  209. nautobot/project-static/docs/development/core/style-guide.html +23 -0
  210. nautobot/project-static/docs/development/core/templates.html +23 -0
  211. nautobot/project-static/docs/development/core/testing.html +23 -0
  212. nautobot/project-static/docs/development/core/ui-component-framework.html +713 -255
  213. nautobot/project-static/docs/development/core/user-preferences.html +23 -0
  214. nautobot/project-static/docs/development/index.html +23 -0
  215. nautobot/project-static/docs/development/jobs/getting-started.html +23 -0
  216. nautobot/project-static/docs/development/jobs/index.html +23 -0
  217. nautobot/project-static/docs/development/jobs/installation.html +23 -0
  218. nautobot/project-static/docs/development/jobs/job-extensions.html +23 -0
  219. nautobot/project-static/docs/development/jobs/job-logging.html +23 -0
  220. nautobot/project-static/docs/development/jobs/job-patterns.html +23 -0
  221. nautobot/project-static/docs/development/jobs/job-structure.html +23 -0
  222. nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -0
  223. nautobot/project-static/docs/development/jobs/testing.html +23 -0
  224. nautobot/project-static/docs/index.html +23 -0
  225. nautobot/project-static/docs/media/development/core/ui-component-framework/breadcrumbs-titles-data-flow.png +0 -0
  226. nautobot/project-static/docs/media/power_distribution.png +0 -0
  227. nautobot/project-static/docs/objects.inv +0 -0
  228. nautobot/project-static/docs/overview/application_stack.html +23 -0
  229. nautobot/project-static/docs/overview/design_philosophy.html +23 -0
  230. nautobot/project-static/docs/release-notes/index.html +23 -0
  231. nautobot/project-static/docs/release-notes/version-1.0.html +23 -0
  232. nautobot/project-static/docs/release-notes/version-1.1.html +23 -0
  233. nautobot/project-static/docs/release-notes/version-1.2.html +23 -0
  234. nautobot/project-static/docs/release-notes/version-1.3.html +23 -0
  235. nautobot/project-static/docs/release-notes/version-1.4.html +23 -0
  236. nautobot/project-static/docs/release-notes/version-1.5.html +23 -0
  237. nautobot/project-static/docs/release-notes/version-1.6.html +23 -0
  238. nautobot/project-static/docs/release-notes/version-2.0.html +23 -0
  239. nautobot/project-static/docs/release-notes/version-2.1.html +23 -0
  240. nautobot/project-static/docs/release-notes/version-2.2.html +23 -0
  241. nautobot/project-static/docs/release-notes/version-2.3.html +23 -0
  242. nautobot/project-static/docs/release-notes/version-2.4.html +306 -0
  243. nautobot/project-static/docs/requirements.txt +2 -2
  244. nautobot/project-static/docs/search/search_index.json +1 -1
  245. nautobot/project-static/docs/sitemap.xml +303 -299
  246. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  247. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +23 -0
  248. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +23 -0
  249. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +23 -0
  250. nautobot/project-static/docs/user-guide/administration/configuration/index.html +23 -0
  251. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +23 -0
  252. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +23 -0
  253. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +23 -0
  254. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +23 -0
  255. nautobot/project-static/docs/user-guide/administration/guides/docker.html +23 -0
  256. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +23 -0
  257. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +23 -0
  258. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +23 -0
  259. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +23 -0
  260. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +23 -0
  261. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +23 -0
  262. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +23 -0
  263. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +23 -0
  264. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +23 -0
  265. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +23 -0
  266. nautobot/project-static/docs/user-guide/administration/installation/index.html +23 -0
  267. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +23 -0
  268. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +23 -0
  269. nautobot/project-static/docs/user-guide/administration/installation/services.html +23 -0
  270. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +23 -0
  271. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +23 -0
  272. nautobot/project-static/docs/user-guide/administration/security/index.html +23 -0
  273. nautobot/project-static/docs/user-guide/administration/security/notices.html +23 -0
  274. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +284 -219
  275. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +23 -0
  276. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +23 -0
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +23 -0
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +23 -0
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +23 -0
  280. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +23 -0
  281. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +23 -0
  282. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +23 -0
  283. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +23 -0
  284. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +23 -0
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +23 -0
  286. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +23 -0
  287. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +23 -0
  288. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +23 -0
  289. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +23 -0
  290. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +23 -0
  291. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +23 -0
  292. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +23 -0
  293. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +23 -0
  294. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +23 -0
  295. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +23 -0
  296. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +23 -0
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +23 -0
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +23 -0
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +23 -0
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +23 -0
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +23 -0
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +23 -0
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +23 -0
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +23 -0
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +23 -0
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +23 -0
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +23 -0
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +23 -0
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +23 -0
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +23 -0
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +23 -0
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +23 -0
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +23 -0
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +23 -0
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +23 -0
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +23 -0
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +23 -0
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +23 -0
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +23 -0
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +23 -0
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +23 -0
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +23 -0
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +23 -0
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +23 -0
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +305 -5
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +24 -1
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +23 -0
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +136 -3
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +41 -1
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +40 -0
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +23 -0
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +23 -0
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +23 -0
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +23 -0
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +23 -0
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +23 -0
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +23 -0
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +23 -0
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +23 -0
  340. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +23 -0
  341. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +23 -0
  342. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +23 -0
  343. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +23 -0
  344. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +23 -0
  345. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +23 -0
  346. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +23 -0
  347. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +23 -0
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +23 -0
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +23 -0
  350. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +23 -0
  351. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +23 -0
  352. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +23 -0
  353. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +23 -0
  354. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +23 -0
  355. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +23 -0
  356. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +23 -0
  357. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +23 -0
  358. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +23 -0
  359. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +23 -0
  360. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +23 -0
  361. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +23 -0
  362. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +23 -0
  363. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +23 -0
  364. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +23 -0
  365. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +23 -0
  366. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +23 -0
  367. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +23 -0
  368. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +23 -0
  369. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +23 -0
  370. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +23 -0
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +23 -0
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +23 -0
  373. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +23 -0
  374. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +23 -0
  375. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +23 -0
  376. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +23 -0
  377. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +23 -0
  378. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +23 -0
  379. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +23 -0
  380. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +23 -0
  381. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +23 -0
  382. nautobot/project-static/docs/user-guide/index.html +23 -0
  383. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +23 -0
  384. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +23 -0
  385. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +23 -0
  386. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +23 -0
  387. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +23 -0
  388. nautobot/project-static/docs/user-guide/platform-functionality/events.html +23 -0
  389. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +23 -0
  390. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +23 -0
  391. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +23 -0
  392. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +23 -0
  393. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +23 -0
  394. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +23 -0
  395. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +23 -0
  396. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +24 -1
  397. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +23 -0
  398. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +23 -0
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +23 -0
  400. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +23 -0
  401. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +23 -0
  402. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +23 -0
  403. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +23 -0
  404. nautobot/project-static/docs/user-guide/platform-functionality/note.html +23 -0
  405. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +23 -0
  406. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +23 -0
  407. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +23 -0
  408. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +23 -0
  409. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +23 -0
  410. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +23 -0
  411. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +23 -0
  412. nautobot/project-static/docs/user-guide/platform-functionality/role.html +23 -0
  413. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +23 -0
  414. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +23 -0
  415. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +23 -0
  416. nautobot/project-static/docs/user-guide/platform-functionality/status.html +23 -0
  417. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +23 -0
  418. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +23 -0
  419. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +23 -0
  420. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +23 -0
  421. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +23 -0
  422. nautobot/users/tests/test_api.py +2 -2
  423. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -252
  424. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -75
  425. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +252 -0
  426. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +75 -0
  427. nautobot/virtualization/urls.py +3 -61
  428. nautobot/virtualization/views.py +48 -72
  429. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/METADATA +24 -24
  430. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/RECORD +434 -417
  431. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/LICENSE.txt +0 -0
  432. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/NOTICE +0 -0
  433. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/WHEEL +0 -0
  434. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/entry_points.txt +0 -0
@@ -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 PowerFeedPhaseChoices, PowerFeedSupplyChoices, PowerFeedTypeChoices
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(to="PowerPanel", on_delete=models.PROTECT, related_name="power_feeds")
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 = ["power_panel", "name"]
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 Location as PowerPanel
154
- if self.rack and self.rack.location != self.power_panel.location: # pylint: disable=no-member
155
- raise ValidationError(
156
- f"Rack {self.rack} ({self.rack.location}) and " # pylint: disable=no-member
157
- f"power panel {self.power_panel} ({self.power_panel.location}) are in different locations"
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)
@@ -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 = pf_powerports.aggregate(Sum("allocated_draw"))["allocated_draw__sum"] or 0
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"))["allocated_draw__sum"]
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
@@ -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",
@@ -239,6 +239,7 @@ class PowerPortTemplateTable(ComponentTemplateTable):
239
239
  "type",
240
240
  "maximum_draw",
241
241
  "allocated_draw",
242
+ "power_factor",
242
243
  "description",
243
244
  "actions",
244
245
  )
@@ -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 = ("pk", "name", "location", "rack_group", "power_feed_count", "tags")
40
- default_columns = ("pk", "name", "location", "rack_group", "power_feed_count")
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.powerports.exists and object.poweroutlets.exists %}
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.powerports.all %}
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">&mdash;</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">&mdash;</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.location_type.children.all %}
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.location_type.children.all %}
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>