nautobot 2.4.13__py3-none-any.whl → 2.4.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (463) 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/graphql/generators.py +8 -0
  7. nautobot/core/graphql/schema.py +30 -30
  8. nautobot/core/management/commands/check_job_approval_status.py +47 -0
  9. nautobot/core/management/commands/generate_test_data.py +1 -1
  10. nautobot/core/management/commands/migrate.py +90 -1
  11. nautobot/core/models/tree_queries.py +17 -0
  12. nautobot/core/settings.py +2 -2
  13. nautobot/core/settings.yaml +3 -3
  14. nautobot/core/tables.py +29 -6
  15. nautobot/core/templates/base_django.html +1 -1
  16. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  17. nautobot/core/templates/generic/object_list.html +17 -20
  18. nautobot/core/templates/inc/breadcrumbs.html +14 -0
  19. nautobot/core/templatetags/buttons.py +2 -4
  20. nautobot/core/templatetags/helpers.py +29 -6
  21. nautobot/core/templatetags/ui_framework.py +21 -0
  22. nautobot/core/testing/api.py +7 -0
  23. nautobot/core/testing/filters.py +20 -3
  24. nautobot/core/testing/forms.py +1 -1
  25. nautobot/core/tests/integration/test_filters.py +2 -2
  26. nautobot/core/tests/test_breadcrumbs.py +366 -0
  27. nautobot/core/tests/test_commands.py +40 -0
  28. nautobot/core/tests/test_filters.py +51 -1
  29. nautobot/core/tests/test_forms.py +1 -1
  30. nautobot/core/tests/test_graphql.py +4 -4
  31. nautobot/core/tests/test_titles.py +183 -0
  32. nautobot/core/tests/test_tree_queries.py +30 -0
  33. nautobot/core/tests/test_views.py +2 -2
  34. nautobot/core/tests/test_views_utils.py +1 -1
  35. nautobot/core/ui/breadcrumbs.py +538 -0
  36. nautobot/core/ui/bulk_buttons.py +53 -0
  37. nautobot/core/ui/object_detail.py +31 -8
  38. nautobot/core/ui/titles.py +127 -0
  39. nautobot/core/ui/utils.py +25 -0
  40. nautobot/core/utils/migrations.py +1 -1
  41. nautobot/core/views/__init__.py +1 -1
  42. nautobot/core/views/mixins.py +26 -1
  43. nautobot/core/views/renderers.py +20 -2
  44. nautobot/core/views/utils.py +14 -13
  45. nautobot/dcim/api/serializers.py +9 -0
  46. nautobot/dcim/choices.py +55 -0
  47. nautobot/dcim/constants.py +0 -16
  48. nautobot/dcim/factory.py +1 -1
  49. nautobot/dcim/filters/__init__.py +15 -3
  50. nautobot/dcim/forms.py +120 -7
  51. nautobot/dcim/management/commands/trace_paths.py +1 -1
  52. nautobot/dcim/migrations/0072_alter_powerfeed_options_and_more.py +97 -0
  53. nautobot/dcim/models/device_component_templates.py +8 -0
  54. nautobot/dcim/models/device_components.py +31 -12
  55. nautobot/dcim/models/devices.py +1 -1
  56. nautobot/dcim/models/power.py +171 -10
  57. nautobot/dcim/models/racks.py +7 -4
  58. nautobot/dcim/tables/devices.py +2 -0
  59. nautobot/dcim/tables/devicetypes.py +1 -0
  60. nautobot/dcim/tables/power.py +30 -2
  61. nautobot/dcim/templates/dcim/device.html +2 -2
  62. nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -214
  63. nautobot/dcim/templates/dcim/location_retrieve.html +2 -2
  64. nautobot/dcim/templates/dcim/powerfeed_edit.html +8 -0
  65. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -1
  66. nautobot/dcim/templates/dcim/rack.html +2 -318
  67. nautobot/dcim/templates/dcim/rack_edit.html +2 -47
  68. nautobot/dcim/templates/dcim/rack_retrieve.html +318 -0
  69. nautobot/dcim/templates/dcim/rack_update.html +47 -0
  70. nautobot/dcim/tests/integration/test_device_bulk_operations.py +61 -0
  71. nautobot/dcim/tests/test_api.py +24 -4
  72. nautobot/dcim/tests/test_filters.py +91 -13
  73. nautobot/dcim/tests/test_models.py +262 -0
  74. nautobot/dcim/tests/test_views.py +20 -12
  75. nautobot/dcim/urls.py +2 -27
  76. nautobot/dcim/utils.py +13 -30
  77. nautobot/dcim/views.py +428 -146
  78. nautobot/extras/choices.py +12 -4
  79. nautobot/extras/factory.py +19 -20
  80. nautobot/extras/filters/__init__.py +3 -2
  81. nautobot/extras/filters/mixins.py +23 -7
  82. nautobot/extras/forms/__init__.py +2 -1
  83. nautobot/extras/forms/forms.py +71 -0
  84. nautobot/extras/forms/mixins.py +4 -2
  85. nautobot/extras/managers.py +4 -1
  86. nautobot/extras/migrations/0062_collect_roles_from_related_apps_roles.py +30 -7
  87. nautobot/extras/migrations/0124_add_joblogentry_index.py +16 -0
  88. nautobot/extras/migrations/0125_jobresult_date_started.py +18 -0
  89. nautobot/extras/models/customfields.py +53 -5
  90. nautobot/extras/models/datasources.py +1 -2
  91. nautobot/extras/models/jobs.py +13 -3
  92. nautobot/extras/models/relationships.py +55 -6
  93. nautobot/extras/plugins/views.py +24 -1
  94. nautobot/extras/secrets/__init__.py +1 -1
  95. nautobot/extras/tables.py +9 -0
  96. nautobot/extras/templates/extras/customfield.html +2 -129
  97. nautobot/extras/templates/extras/customfield_edit.html +2 -108
  98. nautobot/extras/templates/extras/customfield_retrieve.html +129 -0
  99. nautobot/extras/templates/extras/customfield_update.html +108 -0
  100. nautobot/extras/templates/extras/graphqlquery.html +2 -97
  101. nautobot/extras/templates/extras/graphqlquery_list.html +1 -0
  102. nautobot/extras/templates/extras/graphqlquery_retrieve.html +97 -0
  103. nautobot/extras/templates/extras/inc/jobresult.html +7 -3
  104. nautobot/extras/templates/extras/jobresult.html +2 -155
  105. nautobot/extras/templates/extras/jobresult_retrieve.html +155 -0
  106. nautobot/extras/templates/extras/marketplace.html +5 -6
  107. nautobot/extras/templates/extras/note.html +2 -53
  108. nautobot/extras/templates/extras/note_retrieve.html +53 -0
  109. nautobot/extras/templates/extras/plugins_list.html +5 -6
  110. nautobot/extras/templates/extras/secretsgroup.html +2 -29
  111. nautobot/extras/templates/extras/secretsgroup_edit.html +2 -82
  112. nautobot/extras/templates/extras/secretsgroup_retrieve.html +29 -0
  113. nautobot/extras/templates/extras/secretsgroup_update.html +82 -0
  114. nautobot/extras/templatetags/custom_links.py +2 -2
  115. nautobot/extras/templatetags/job_buttons.py +1 -1
  116. nautobot/extras/templatetags/plugins.py +1 -1
  117. nautobot/extras/tests/integration/test_computedfields.py +2 -2
  118. nautobot/extras/tests/integration/test_customfields.py +14 -11
  119. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  120. nautobot/extras/tests/integration/test_notes.py +1 -1
  121. nautobot/extras/tests/integration/test_plugins.py +6 -6
  122. nautobot/extras/tests/integration/test_relationships.py +2 -2
  123. nautobot/extras/tests/test_customfields.py +115 -7
  124. nautobot/extras/tests/test_filters.py +9 -0
  125. nautobot/extras/tests/test_forms.py +2 -2
  126. nautobot/extras/tests/test_plugins.py +2 -3
  127. nautobot/extras/tests/test_relationships.py +14 -8
  128. nautobot/extras/tests/test_views.py +285 -2
  129. nautobot/extras/urls.py +5 -110
  130. nautobot/extras/utils.py +5 -2
  131. nautobot/extras/views.py +116 -311
  132. nautobot/ipam/api/views.py +69 -6
  133. nautobot/ipam/tables.py +8 -15
  134. nautobot/ipam/tests/migration/test_migrations.py +8 -8
  135. nautobot/ipam/tests/test_api.py +352 -2
  136. nautobot/ipam/tests/test_models.py +1 -1
  137. nautobot/project-static/docs/404.html +34 -34
  138. nautobot/project-static/docs/apps/index.html +34 -34
  139. nautobot/project-static/docs/apps/nautobot-apps.html +34 -34
  140. nautobot/project-static/docs/assets/_mkdocstrings.css +44 -6
  141. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js → bundle.50899def.min.js} +2 -2
  142. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js.map → bundle.50899def.min.js.map} +2 -2
  143. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css → main.7e37652d.min.css} +1 -1
  144. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css.map → main.7e37652d.min.css.map} +1 -1
  145. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +39 -34
  146. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +36 -34
  147. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +139 -54
  148. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +48 -38
  149. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +50 -40
  150. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +36 -34
  151. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +35 -34
  152. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +43 -39
  153. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +52 -42
  154. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +50 -41
  155. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +54 -44
  156. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +85 -93
  157. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +154 -62
  158. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +54 -46
  159. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +146 -87
  160. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +240 -70
  161. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +38 -35
  162. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +41 -35
  163. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +173 -52
  164. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +269 -85
  165. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5987 -2643
  166. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +36 -34
  167. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +165 -89
  168. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +161 -69
  169. nautobot/project-static/docs/development/apps/api/configuration-view.html +34 -34
  170. nautobot/project-static/docs/development/apps/api/database-backend-config.html +34 -34
  171. nautobot/project-static/docs/development/apps/api/models/django-admin.html +34 -34
  172. nautobot/project-static/docs/development/apps/api/models/global-search.html +34 -34
  173. nautobot/project-static/docs/development/apps/api/models/graphql.html +34 -34
  174. nautobot/project-static/docs/development/apps/api/models/index.html +34 -34
  175. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +34 -34
  176. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +34 -34
  177. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +34 -34
  178. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +34 -34
  179. nautobot/project-static/docs/development/apps/api/platform-features/index.html +34 -34
  180. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +34 -34
  181. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +34 -34
  182. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +34 -34
  183. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +34 -34
  184. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +34 -34
  185. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +34 -34
  186. nautobot/project-static/docs/development/apps/api/prometheus.html +34 -34
  187. nautobot/project-static/docs/development/apps/api/setup.html +34 -34
  188. nautobot/project-static/docs/development/apps/api/testing.html +34 -34
  189. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +34 -34
  190. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +34 -34
  191. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +34 -34
  192. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +34 -34
  193. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +34 -34
  194. nautobot/project-static/docs/development/apps/api/views/base-template.html +34 -34
  195. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +34 -34
  196. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +34 -34
  197. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +34 -34
  198. nautobot/project-static/docs/development/apps/api/views/index.html +34 -34
  199. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +34 -34
  200. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +42 -36
  201. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +34 -34
  202. nautobot/project-static/docs/development/apps/api/views/notes.html +34 -34
  203. nautobot/project-static/docs/development/apps/api/views/rest-api.html +34 -34
  204. nautobot/project-static/docs/development/apps/api/views/urls.html +34 -34
  205. nautobot/project-static/docs/development/apps/index.html +34 -34
  206. nautobot/project-static/docs/development/apps/migration/code-updates.html +34 -34
  207. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +34 -34
  208. nautobot/project-static/docs/development/apps/migration/from-v1.html +34 -34
  209. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +34 -34
  210. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +34 -34
  211. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +34 -34
  212. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +34 -34
  213. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +37 -37
  214. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +10544 -0
  215. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +34 -34
  216. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +34 -34
  217. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +34 -34
  218. nautobot/project-static/docs/development/apps/porting-from-netbox.html +37 -37
  219. nautobot/project-static/docs/development/core/application-registry.html +162 -133
  220. nautobot/project-static/docs/development/core/best-practices.html +34 -34
  221. nautobot/project-static/docs/development/core/bootstrap-ui.html +34 -34
  222. nautobot/project-static/docs/development/core/caching.html +34 -34
  223. nautobot/project-static/docs/development/core/controllers.html +34 -34
  224. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +34 -34
  225. nautobot/project-static/docs/development/core/generic-views.html +34 -34
  226. nautobot/project-static/docs/development/core/getting-started.html +34 -34
  227. nautobot/project-static/docs/development/core/homepage.html +34 -34
  228. nautobot/project-static/docs/development/core/index.html +34 -34
  229. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +34 -34
  230. nautobot/project-static/docs/development/core/model-checklist.html +34 -34
  231. nautobot/project-static/docs/development/core/model-features.html +34 -34
  232. nautobot/project-static/docs/development/core/natural-keys.html +34 -34
  233. nautobot/project-static/docs/development/core/navigation-menu.html +34 -34
  234. nautobot/project-static/docs/development/core/release-checklist.html +34 -34
  235. nautobot/project-static/docs/development/core/role-internals.html +34 -34
  236. nautobot/project-static/docs/development/core/settings.html +34 -34
  237. nautobot/project-static/docs/development/core/style-guide.html +34 -34
  238. nautobot/project-static/docs/development/core/templates.html +34 -34
  239. nautobot/project-static/docs/development/core/testing.html +34 -34
  240. nautobot/project-static/docs/development/core/ui-component-framework.html +724 -289
  241. nautobot/project-static/docs/development/core/user-preferences.html +34 -34
  242. nautobot/project-static/docs/development/index.html +34 -34
  243. nautobot/project-static/docs/development/jobs/getting-started.html +34 -34
  244. nautobot/project-static/docs/development/jobs/index.html +34 -34
  245. nautobot/project-static/docs/development/jobs/installation.html +34 -34
  246. nautobot/project-static/docs/development/jobs/job-extensions.html +34 -34
  247. nautobot/project-static/docs/development/jobs/job-logging.html +34 -34
  248. nautobot/project-static/docs/development/jobs/job-patterns.html +34 -34
  249. nautobot/project-static/docs/development/jobs/job-structure.html +34 -34
  250. nautobot/project-static/docs/development/jobs/migration/from-v1.html +34 -34
  251. nautobot/project-static/docs/development/jobs/testing.html +34 -34
  252. nautobot/project-static/docs/index.html +34 -34
  253. nautobot/project-static/docs/media/development/core/ui-component-framework/breadcrumbs-titles-data-flow.png +0 -0
  254. nautobot/project-static/docs/media/power_distribution.png +0 -0
  255. nautobot/project-static/docs/objects.inv +0 -0
  256. nautobot/project-static/docs/overview/application_stack.html +34 -34
  257. nautobot/project-static/docs/overview/design_philosophy.html +34 -34
  258. nautobot/project-static/docs/release-notes/index.html +34 -34
  259. nautobot/project-static/docs/release-notes/version-1.0.html +34 -34
  260. nautobot/project-static/docs/release-notes/version-1.1.html +34 -34
  261. nautobot/project-static/docs/release-notes/version-1.2.html +34 -34
  262. nautobot/project-static/docs/release-notes/version-1.3.html +34 -34
  263. nautobot/project-static/docs/release-notes/version-1.4.html +34 -34
  264. nautobot/project-static/docs/release-notes/version-1.5.html +34 -34
  265. nautobot/project-static/docs/release-notes/version-1.6.html +34 -34
  266. nautobot/project-static/docs/release-notes/version-2.0.html +34 -34
  267. nautobot/project-static/docs/release-notes/version-2.1.html +34 -34
  268. nautobot/project-static/docs/release-notes/version-2.2.html +34 -34
  269. nautobot/project-static/docs/release-notes/version-2.3.html +34 -34
  270. nautobot/project-static/docs/release-notes/version-2.4.html +402 -34
  271. nautobot/project-static/docs/requirements.txt +3 -3
  272. nautobot/project-static/docs/search/search_index.json +1 -1
  273. nautobot/project-static/docs/sitemap.xml +303 -299
  274. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  275. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +34 -34
  276. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +34 -34
  277. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +34 -34
  278. nautobot/project-static/docs/user-guide/administration/configuration/index.html +34 -34
  279. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +34 -34
  280. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +37 -37
  281. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +34 -34
  282. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +34 -34
  283. nautobot/project-static/docs/user-guide/administration/guides/docker.html +34 -34
  284. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +34 -34
  285. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +34 -34
  286. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +34 -34
  287. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +34 -34
  288. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +34 -34
  289. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +34 -34
  290. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +34 -34
  291. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +34 -34
  292. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +34 -34
  293. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +34 -34
  294. nautobot/project-static/docs/user-guide/administration/installation/index.html +34 -34
  295. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +34 -34
  296. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +34 -34
  297. nautobot/project-static/docs/user-guide/administration/installation/services.html +34 -34
  298. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +34 -34
  299. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +34 -34
  300. nautobot/project-static/docs/user-guide/administration/security/index.html +34 -34
  301. nautobot/project-static/docs/user-guide/administration/security/notices.html +34 -34
  302. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +296 -251
  303. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +34 -34
  304. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +34 -34
  305. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +34 -34
  306. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +34 -34
  307. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +34 -34
  308. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +34 -34
  309. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +34 -34
  310. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +34 -34
  311. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +34 -34
  312. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +34 -34
  313. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +34 -34
  314. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +34 -34
  315. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +34 -34
  316. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +34 -34
  317. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +34 -34
  318. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +34 -34
  319. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +34 -34
  320. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +34 -34
  321. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +34 -34
  322. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +34 -34
  323. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +34 -34
  324. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +34 -34
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +34 -34
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +34 -34
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +34 -34
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +34 -34
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +34 -34
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +34 -34
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +34 -34
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +34 -34
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +34 -34
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +34 -34
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +34 -34
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +34 -34
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +34 -34
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +34 -34
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +34 -34
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +37 -37
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +34 -34
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +34 -34
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +42 -52
  344. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +34 -34
  345. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +34 -34
  346. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +34 -34
  347. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +34 -34
  348. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +37 -37
  349. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +34 -34
  350. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +34 -34
  351. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +34 -34
  352. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +34 -34
  353. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +316 -39
  354. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +35 -35
  355. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +34 -34
  356. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +147 -37
  357. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +52 -35
  358. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +51 -34
  359. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +34 -34
  360. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +34 -34
  361. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +34 -34
  362. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +34 -34
  363. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +34 -34
  364. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +34 -34
  365. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +34 -34
  366. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +34 -34
  367. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +34 -34
  368. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +34 -34
  369. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +34 -34
  370. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +34 -34
  371. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +34 -34
  372. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +34 -34
  373. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +34 -34
  374. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +34 -34
  375. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +34 -34
  376. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +34 -34
  377. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +34 -34
  378. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +34 -34
  379. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +34 -34
  380. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +34 -34
  381. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +34 -34
  382. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +34 -34
  383. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +34 -34
  384. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +34 -34
  385. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +34 -34
  386. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +34 -34
  387. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +34 -34
  388. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +34 -34
  389. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +34 -34
  390. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +34 -34
  391. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +34 -34
  392. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +34 -34
  393. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +34 -34
  394. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +34 -34
  395. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +34 -34
  396. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +34 -34
  397. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +34 -34
  398. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +34 -34
  399. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +34 -34
  400. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +34 -34
  401. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +34 -34
  402. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +34 -34
  403. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +34 -34
  404. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +34 -34
  405. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +34 -34
  406. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +34 -34
  407. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +34 -34
  408. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +34 -34
  409. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +34 -34
  410. nautobot/project-static/docs/user-guide/index.html +34 -34
  411. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +34 -34
  412. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +34 -34
  413. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +34 -34
  414. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +34 -34
  415. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +34 -34
  416. nautobot/project-static/docs/user-guide/platform-functionality/events.html +34 -34
  417. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +34 -34
  418. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +34 -34
  419. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +34 -34
  420. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +34 -34
  421. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +34 -34
  422. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +34 -34
  423. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +34 -34
  424. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +35 -35
  425. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +34 -34
  426. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +34 -34
  427. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +34 -34
  428. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +34 -34
  429. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +34 -34
  430. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +34 -34
  431. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +34 -34
  432. nautobot/project-static/docs/user-guide/platform-functionality/note.html +34 -34
  433. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +34 -34
  434. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +34 -34
  435. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +34 -34
  436. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +34 -34
  437. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +34 -34
  438. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +34 -34
  439. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +34 -34
  440. nautobot/project-static/docs/user-guide/platform-functionality/role.html +34 -34
  441. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +34 -34
  442. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +34 -34
  443. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +34 -34
  444. nautobot/project-static/docs/user-guide/platform-functionality/status.html +34 -34
  445. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +34 -34
  446. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +34 -34
  447. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +34 -34
  448. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +34 -34
  449. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +34 -34
  450. nautobot/tenancy/api/views.py +2 -1
  451. nautobot/users/tests/test_api.py +2 -2
  452. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -252
  453. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -75
  454. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +252 -0
  455. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +75 -0
  456. nautobot/virtualization/urls.py +3 -61
  457. nautobot/virtualization/views.py +48 -72
  458. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/METADATA +27 -27
  459. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/RECORD +463 -439
  460. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/LICENSE.txt +0 -0
  461. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/NOTICE +0 -0
  462. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/WHEEL +0 -0
  463. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py CHANGED
@@ -37,6 +37,17 @@ from nautobot.core.models.querysets import count_related
37
37
  from nautobot.core.templatetags import helpers
38
38
  from nautobot.core.templatetags.helpers import has_perms
39
39
  from nautobot.core.ui import object_detail
40
+ from nautobot.core.ui.breadcrumbs import (
41
+ BaseBreadcrumbItem,
42
+ Breadcrumbs,
43
+ InstanceBreadcrumbItem,
44
+ ModelBreadcrumbItem,
45
+ )
46
+ from nautobot.core.ui.bulk_buttons import (
47
+ BulkDeleteButton,
48
+ BulkEditButton,
49
+ BulkRenameButton,
50
+ )
40
51
  from nautobot.core.ui.choices import SectionChoices
41
52
  from nautobot.core.utils.lookup import get_form_for_model
42
53
  from nautobot.core.utils.permissions import get_permission_for_model
@@ -55,9 +66,11 @@ from nautobot.core.views.mixins import (
55
66
  ObjectPermissionRequiredMixin,
56
67
  )
57
68
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
69
+ from nautobot.core.views.utils import get_obj_from_context
58
70
  from nautobot.core.views.viewsets import NautobotUIViewSet
59
71
  from nautobot.dcim.choices import LocationDataToContactActionChoices
60
72
  from nautobot.dcim.forms import LocationMigrateDataToContactForm
73
+ from nautobot.dcim.utils import get_all_network_driver_mappings
61
74
  from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
62
75
  from nautobot.extras.tables import DynamicGroupTable
63
76
  from nautobot.extras.views import ObjectChangeLogView, ObjectConfigContextView, ObjectDynamicGroupsView
@@ -256,10 +269,11 @@ class LocationTypeUIViewSet(NautobotUIViewSet):
256
269
 
257
270
 
258
271
  class LocationUIViewSet(NautobotUIViewSet):
259
- # We aren't accessing tree fields anywhere so this is safe (note that `parent` itself is a normal foreign
260
- # key, not a tree field). If we ever do access tree fields, this will perform worse, because django will
272
+ # We are only accessing the tree fields from the list view, where `with_tree_fields` is called dynamically
273
+ # depending on whether the hierarchy is shown in the UI (note that `parent` itself is a normal foreign key, not a
274
+ # tree field). If we ever do access tree fields elsewhere, this will perform worse, because django will
261
275
  # automatically issue a second query (similar to behavior for
262
- # https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.only)
276
+ # https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.only).
263
277
  queryset = Location.objects.without_tree_fields().select_related("location_type", "parent", "tenant")
264
278
  filterset_class = filters.LocationFilterSet
265
279
  filterset_form_class = forms.LocationFilterForm
@@ -524,11 +538,41 @@ class RackGroupUIViewSet(NautobotUIViewSet):
524
538
  #
525
539
 
526
540
 
527
- class RackListView(generic.ObjectListView):
528
- queryset = Rack.objects.all()
529
- filterset = filters.RackFilterSet
530
- filterset_form = forms.RackFilterForm
531
- table = tables.RackDetailTable
541
+ class RackUIViewSet(NautobotUIViewSet):
542
+ bulk_update_form_class = forms.RackBulkEditForm
543
+ filterset_class = filters.RackFilterSet
544
+ filterset_form_class = forms.RackFilterForm
545
+ form_class = forms.RackForm
546
+ serializer_class = serializers.RackSerializer
547
+ table_class = tables.RackDetailTable
548
+ queryset = Rack.objects.select_related("location", "tenant__tenant_group", "rack_group", "role")
549
+
550
+ def get_extra_context(self, request, instance):
551
+ context = super().get_extra_context(request, instance)
552
+
553
+ if self.action == "retrieve":
554
+ # Get 0U and child devices located within the rack
555
+ context["nonracked_devices"] = Device.objects.filter(rack=instance, position__isnull=True).select_related(
556
+ "device_type__manufacturer"
557
+ )
558
+
559
+ peer_racks = Rack.objects.restrict(request.user, "view").filter(location=instance.location)
560
+
561
+ if instance.rack_group:
562
+ peer_racks = peer_racks.filter(rack_group=instance.rack_group)
563
+ else:
564
+ peer_racks = peer_racks.filter(rack_group__isnull=True)
565
+
566
+ context["next_rack"] = peer_racks.filter(name__gt=instance.name).order_by("name").first()
567
+ context["prev_rack"] = peer_racks.filter(name__lt=instance.name).order_by("-name").first()
568
+
569
+ context["reservations"] = RackReservation.objects.restrict(request.user, "view").filter(rack=instance)
570
+ context["power_feeds"] = (
571
+ PowerFeed.objects.restrict(request.user, "view").filter(rack=instance).select_related("power_panel")
572
+ )
573
+ context["device_count"] = Device.objects.restrict(request.user, "view").filter(rack=instance).count()
574
+
575
+ return context
532
576
 
533
577
 
534
578
  class RackElevationListView(generic.ObjectListView):
@@ -584,70 +628,6 @@ class RackElevationListView(generic.ObjectListView):
584
628
  }
585
629
 
586
630
 
587
- class RackView(generic.ObjectView):
588
- queryset = Rack.objects.select_related("location", "tenant__tenant_group", "rack_group", "role")
589
-
590
- def get_extra_context(self, request, instance):
591
- # Get 0U and child devices located within the rack
592
- nonracked_devices = Device.objects.filter(rack=instance, position__isnull=True).select_related(
593
- "device_type__manufacturer"
594
- )
595
-
596
- peer_racks = Rack.objects.restrict(request.user, "view").filter(location=instance.location)
597
-
598
- if instance.rack_group:
599
- peer_racks = peer_racks.filter(rack_group=instance.rack_group)
600
- else:
601
- peer_racks = peer_racks.filter(rack_group__isnull=True)
602
- next_rack = peer_racks.filter(name__gt=instance.name).order_by("name").first()
603
- prev_rack = peer_racks.filter(name__lt=instance.name).order_by("-name").first()
604
-
605
- reservations = RackReservation.objects.restrict(request.user, "view").filter(rack=instance)
606
- power_feeds = (
607
- PowerFeed.objects.restrict(request.user, "view").filter(rack=instance).select_related("power_panel")
608
- )
609
-
610
- device_count = Device.objects.restrict(request.user, "view").filter(rack=instance).count()
611
-
612
- return {
613
- "device_count": device_count,
614
- "reservations": reservations,
615
- "power_feeds": power_feeds,
616
- "nonracked_devices": nonracked_devices,
617
- "next_rack": next_rack,
618
- "prev_rack": prev_rack,
619
- **super().get_extra_context(request, instance),
620
- }
621
-
622
-
623
- class RackEditView(generic.ObjectEditView):
624
- queryset = Rack.objects.all()
625
- model_form = forms.RackForm
626
- template_name = "dcim/rack_edit.html"
627
-
628
-
629
- class RackDeleteView(generic.ObjectDeleteView):
630
- queryset = Rack.objects.all()
631
-
632
-
633
- class RackBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
634
- queryset = Rack.objects.all()
635
- table = tables.RackTable
636
-
637
-
638
- class RackBulkEditView(generic.BulkEditView):
639
- queryset = Rack.objects.all()
640
- filterset = filters.RackFilterSet
641
- table = tables.RackTable
642
- form = forms.RackBulkEditForm
643
-
644
-
645
- class RackBulkDeleteView(generic.BulkDeleteView):
646
- queryset = Rack.objects.all()
647
- filterset = filters.RackFilterSet
648
- table = tables.RackTable
649
-
650
-
651
631
  #
652
632
  # Rack reservations
653
633
  #
@@ -758,6 +738,173 @@ class ManufacturerUIViewSet(NautobotUIViewSet):
758
738
  #
759
739
  # Device types
760
740
  #
741
+
742
+
743
+ class DeviceTypeFieldsPanel(object_detail.ObjectFieldsPanel):
744
+ """
745
+ Custom panel for DeviceType that renders front_image and rear_image
746
+ as image previews with links, and falls back to normal rendering for other fields.
747
+ """
748
+
749
+ def render_value(self, key, value, context):
750
+ obj = get_obj_from_context(context, self.context_object_key)
751
+
752
+ if key in ["front_image", "rear_image"]:
753
+ image = getattr(obj, key, None)
754
+ if image:
755
+ return format_html(
756
+ '<a href="{}" target="_blank"><img src="{}" alt="{}" class="img-responsive"></a>',
757
+ image.url,
758
+ image.url,
759
+ image.name,
760
+ )
761
+ return format_html('<span class="text-muted">&mdash;</span>')
762
+
763
+ return super().render_value(key, value, context)
764
+
765
+
766
+ # --- Bulk Action Button Base Class ---
767
+ def bulk_footer_buttons(form_id: str, model):
768
+ """Return all bulk action buttons for a given form+model."""
769
+ return [
770
+ BulkRenameButton(form_id=form_id, model=model),
771
+ BulkEditButton(form_id=form_id, model=model),
772
+ BulkDeleteButton(form_id=form_id, model=model),
773
+ ]
774
+
775
+
776
+ # --- Tab Configuration ---
777
+ TAB_CONFIGS = [
778
+ (
779
+ 100,
780
+ "interfaces",
781
+ "Interfaces",
782
+ "dcim:devicetype_interfaces",
783
+ "interface_templates",
784
+ tables.InterfaceTemplateTable,
785
+ InterfaceTemplate,
786
+ ),
787
+ (
788
+ 200,
789
+ "frontports",
790
+ "Front Ports",
791
+ "dcim:devicetype_frontports",
792
+ "front_port_templates",
793
+ tables.FrontPortTemplateTable,
794
+ FrontPortTemplate,
795
+ ),
796
+ (
797
+ 300,
798
+ "rearports",
799
+ "Rear Ports",
800
+ "dcim:devicetype_rearports",
801
+ "rear_port_templates",
802
+ tables.RearPortTemplateTable,
803
+ RearPortTemplate,
804
+ ),
805
+ (
806
+ 400,
807
+ "consoleports",
808
+ "Console Ports",
809
+ "dcim:devicetype_consoleports",
810
+ "console_port_templates",
811
+ tables.ConsolePortTemplateTable,
812
+ ConsolePortTemplate,
813
+ ),
814
+ (
815
+ 500,
816
+ "consoleserverports",
817
+ "Console Server Ports",
818
+ "dcim:devicetype_consoleserverports",
819
+ "console_server_port_templates",
820
+ tables.ConsoleServerPortTemplateTable,
821
+ ConsoleServerPortTemplate,
822
+ ),
823
+ (
824
+ 600,
825
+ "powerports",
826
+ "Power Ports",
827
+ "dcim:devicetype_powerports",
828
+ "power_port_templates",
829
+ tables.PowerPortTemplateTable,
830
+ PowerPortTemplate,
831
+ ),
832
+ (
833
+ 700,
834
+ "poweroutlets",
835
+ "Power Outlets",
836
+ "dcim:devicetype_poweroutlets",
837
+ "power_outlet_templates",
838
+ tables.PowerOutletTemplateTable,
839
+ PowerOutletTemplate,
840
+ ),
841
+ (
842
+ 800,
843
+ "devicebays",
844
+ "Device Bays",
845
+ "dcim:devicetype_devicebays",
846
+ "device_bay_templates",
847
+ tables.DeviceBayTemplateTable,
848
+ DeviceBayTemplate,
849
+ ),
850
+ (
851
+ 900,
852
+ "modulebays",
853
+ "Module Bays",
854
+ "dcim:devicetype_modulebays",
855
+ "module_bay_templates",
856
+ tables.ModuleBayTemplateTable,
857
+ ModuleBayTemplate,
858
+ ),
859
+ ]
860
+
861
+
862
+ # --- Add Components Button Config ---
863
+ ADD_COMPONENTS_CONFIG = [
864
+ (100, "dcim:consoleporttemplate_add", "Console Ports", "mdi-console", ["dcim.add_consoleporttemplate"]),
865
+ (
866
+ 200,
867
+ "dcim:consoleserverporttemplate_add",
868
+ "Console Server Ports",
869
+ "mdi-console-network-outline",
870
+ ["dcim.add_consoleserverporttemplate"],
871
+ ),
872
+ (300, "dcim:powerporttemplate_add", "Power Ports", "mdi-power-plug-outline", ["dcim.add_powerporttemplate"]),
873
+ (400, "dcim:poweroutlettemplate_add", "Power Outlets", "mdi-power-socket", ["dcim.add_poweroutlettemplate"]),
874
+ (500, "dcim:interfacetemplate_add", "Interfaces", "mdi-ethernet", ["dcim.add_interfacetemplate"]),
875
+ (600, "dcim:frontporttemplate_add", "Front Ports", "mdi-square-rounded-outline", ["dcim.add_frontporttemplate"]),
876
+ (700, "dcim:rearporttemplate_add", "Rear Ports", "mdi-square-rounded-outline", ["dcim.add_rearporttemplate"]),
877
+ (800, "dcim:devicebaytemplate_add", "Device Bays", "mdi-circle-outline", ["dcim.add_devicebaytemplate"]),
878
+ (900, "dcim:modulebaytemplate_add", "Module Bays", "mdi-tray", ["dcim.add_modulebaytemplate"]),
879
+ ]
880
+
881
+
882
+ def make_bulk_tab(weight, tab_name, label, url_name, related_attr, table_class, model):
883
+ """Build a bulk-enabled tab."""
884
+ form_id = f"{tab_name}template_form"
885
+ return object_detail.DistinctViewTab(
886
+ weight=weight,
887
+ tab_id=tab_name,
888
+ label=label,
889
+ url_name=url_name,
890
+ related_object_attribute=related_attr,
891
+ panels=(
892
+ object_detail.ObjectsTablePanel(
893
+ section=SectionChoices.FULL_WIDTH,
894
+ weight=100,
895
+ table_title=label,
896
+ table_class=table_class,
897
+ table_filter="device_type",
898
+ tab_id=tab_name,
899
+ enable_bulk_actions=True,
900
+ form_id=form_id,
901
+ footer_buttons=bulk_footer_buttons(form_id=form_id, model=model),
902
+ ),
903
+ ),
904
+ )
905
+
906
+
907
+ # --- DeviceType UI ViewSet ---
761
908
  class DeviceTypeUIViewSet(NautobotUIViewSet):
762
909
  bulk_update_form_class = forms.DeviceTypeBulkEditForm
763
910
  filterset_class = filters.DeviceTypeFilterSet
@@ -767,81 +914,147 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
767
914
  table_class = tables.DeviceTypeTable
768
915
  queryset = DeviceType.objects.select_related("manufacturer").prefetch_related("software_image_files")
769
916
 
770
- def get_extra_context(self, request, instance):
771
- if self.action != "retrieve":
772
- return {}
773
- instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
917
+ object_detail_content = object_detail.ObjectDetailContent(
918
+ panels=(
919
+ DeviceTypeFieldsPanel(
920
+ section=SectionChoices.LEFT_HALF,
921
+ weight=100,
922
+ fields="__all__",
923
+ ),
924
+ object_detail.ObjectsTablePanel(
925
+ section=SectionChoices.RIGHT_HALF,
926
+ weight=200,
927
+ table_class=tables.SoftwareImageFileTable,
928
+ table_filter="device_types",
929
+ select_related_fields=["software_version", "status"],
930
+ exclude_columns=["actions", "tags"],
931
+ related_field_name="device_types",
932
+ add_button_route=None,
933
+ ),
934
+ object_detail.ObjectsTablePanel(
935
+ weight=300,
936
+ section=SectionChoices.FULL_WIDTH,
937
+ table_class=tables.DeviceTable,
938
+ table_filter="device_type",
939
+ related_field_name="device_type",
940
+ table_title="Device Instances",
941
+ exclude_columns=["actions", "tags"],
942
+ ),
943
+ ),
944
+ extra_tabs=tuple(make_bulk_tab(*cfg) for cfg in TAB_CONFIGS),
945
+ extra_buttons=(
946
+ object_detail.DropdownButton(
947
+ weight=100,
948
+ color=ButtonColorChoices.BLUE,
949
+ label="Add Components",
950
+ attributes={"id": "device-type-add-components-button"},
951
+ icon="mdi-plus-thick",
952
+ required_permissions=["dcim.change_devicetype"],
953
+ children=tuple(
954
+ object_detail.Button(
955
+ weight=weight,
956
+ link_name=link_name,
957
+ label=label,
958
+ icon=icon,
959
+ required_permissions=perms,
960
+ link_includes_pk=False,
961
+ )
962
+ for weight, link_name, label, icon, perms in ADD_COMPONENTS_CONFIG
963
+ ),
964
+ ),
965
+ ),
966
+ )
774
967
 
775
- # Component tables
776
- consoleport_table = tables.ConsolePortTemplateTable(
777
- ConsolePortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
778
- orderable=False,
779
- )
780
- consoleserverport_table = tables.ConsoleServerPortTemplateTable(
781
- ConsoleServerPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
782
- orderable=False,
783
- )
784
- powerport_table = tables.PowerPortTemplateTable(
785
- PowerPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
786
- orderable=False,
787
- )
788
- poweroutlet_table = tables.PowerOutletTemplateTable(
789
- PowerOutletTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
790
- orderable=False,
791
- )
792
- interface_table = tables.InterfaceTemplateTable(
793
- list(InterfaceTemplate.objects.restrict(request.user, "view").filter(device_type=instance)),
794
- orderable=False,
795
- )
796
- front_port_table = tables.FrontPortTemplateTable(
797
- FrontPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
798
- orderable=False,
799
- )
800
- rear_port_table = tables.RearPortTemplateTable(
801
- RearPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
802
- orderable=False,
803
- )
804
- devicebay_table = tables.DeviceBayTemplateTable(
805
- DeviceBayTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
806
- orderable=False,
807
- )
808
- modulebay_table = tables.ModuleBayTemplateTable(
809
- ModuleBayTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
810
- orderable=False,
811
- )
812
- if request.user.has_perm("dcim.change_devicetype"):
813
- consoleport_table.columns.show("pk")
814
- consoleserverport_table.columns.show("pk")
815
- powerport_table.columns.show("pk")
816
- poweroutlet_table.columns.show("pk")
817
- interface_table.columns.show("pk")
818
- front_port_table.columns.show("pk")
819
- rear_port_table.columns.show("pk")
820
- devicebay_table.columns.show("pk")
821
- modulebay_table.columns.show("pk")
968
+ # View actions
969
+ @action(
970
+ detail=True,
971
+ methods=["get"],
972
+ url_path="interfaces",
973
+ custom_view_base_action="view",
974
+ custom_view_additional_permissions=["dcim.view_interfacetemplate"],
975
+ )
976
+ def interfaces(self, request, *args, **kwargs):
977
+ return Response({})
822
978
 
823
- software_image_files_table = tables.SoftwareImageFileTable(
824
- instance.software_image_files.restrict(request.user, "view").annotate(
825
- device_type_count=count_related(DeviceType, "software_image_files"),
826
- ),
827
- orderable=False,
828
- exclude=["actions", "tags"],
829
- )
979
+ @action(
980
+ detail=True,
981
+ methods=["get"],
982
+ url_path="frontports",
983
+ custom_view_base_action="view",
984
+ custom_view_additional_permissions=["dcim.view_frontporttemplate"],
985
+ )
986
+ def frontports(self, request, *args, **kwargs):
987
+ return Response({})
830
988
 
831
- return {
832
- "instance_count": instance_count,
833
- "consoleport_table": consoleport_table,
834
- "consoleserverport_table": consoleserverport_table,
835
- "powerport_table": powerport_table,
836
- "poweroutlet_table": poweroutlet_table,
837
- "interface_table": interface_table,
838
- "front_port_table": front_port_table,
839
- "rear_port_table": rear_port_table,
840
- "devicebay_table": devicebay_table,
841
- "modulebay_table": modulebay_table,
842
- "software_image_files_table": software_image_files_table,
843
- **super().get_extra_context(request, instance),
844
- }
989
+ @action(
990
+ detail=True,
991
+ methods=["get"],
992
+ url_path="rearports",
993
+ custom_view_base_action="view",
994
+ custom_view_additional_permissions=["dcim.view_rearporttemplate"],
995
+ )
996
+ def rearports(self, request, *args, **kwargs):
997
+ return Response({})
998
+
999
+ @action(
1000
+ detail=True,
1001
+ methods=["get"],
1002
+ url_path="consoleports",
1003
+ custom_view_base_action="view",
1004
+ custom_view_additional_permissions=["dcim.view_consoleporttemplate"],
1005
+ )
1006
+ def consoleports(self, request, *args, **kwargs):
1007
+ return Response({})
1008
+
1009
+ @action(
1010
+ detail=True,
1011
+ methods=["get"],
1012
+ url_path="consoleserverports",
1013
+ custom_view_base_action="view",
1014
+ custom_view_additional_permissions=["dcim.view_consoleserverporttemplate"],
1015
+ )
1016
+ def consoleserverports(self, request, *args, **kwargs):
1017
+ return Response({})
1018
+
1019
+ @action(
1020
+ detail=True,
1021
+ methods=["get"],
1022
+ url_path="powerports",
1023
+ custom_view_base_action="view",
1024
+ custom_view_additional_permissions=["dcim.view_powerporttemplate"],
1025
+ )
1026
+ def powerports(self, request, *args, **kwargs):
1027
+ return Response({})
1028
+
1029
+ @action(
1030
+ detail=True,
1031
+ methods=["get"],
1032
+ url_path="poweroutlets",
1033
+ custom_view_base_action="view",
1034
+ custom_view_additional_permissions=["dcim.view_poweroutlettemplate"],
1035
+ )
1036
+ def poweroutlets(self, request, *args, **kwargs):
1037
+ return Response({})
1038
+
1039
+ @action(
1040
+ detail=True,
1041
+ methods=["get"],
1042
+ url_path="devicebays",
1043
+ custom_view_base_action="view",
1044
+ custom_view_additional_permissions=["dcim.view_devicebaytemplate"],
1045
+ )
1046
+ def devicebays(self, request, *args, **kwargs):
1047
+ return Response({})
1048
+
1049
+ @action(
1050
+ detail=True,
1051
+ methods=["get"],
1052
+ url_path="modulebays",
1053
+ custom_view_base_action="view",
1054
+ custom_view_additional_permissions=["dcim.view_modulebaytemplate"],
1055
+ )
1056
+ def modulebays(self, request, *args, **kwargs):
1057
+ return Response({})
845
1058
 
846
1059
 
847
1060
  class DeviceTypeImportView(generic.ObjectImportView):
@@ -1666,6 +1879,8 @@ class PlatformUIViewSet(NautobotUIViewSet):
1666
1879
  context = super().get_extra_context(request, instance)
1667
1880
  if self.action == "retrieve":
1668
1881
  context["network_driver_tool_names"] = instance.fetch_network_driver_mappings()
1882
+ if self.action in ["create", "update"]:
1883
+ context["network_driver_names"] = sorted(get_all_network_driver_mappings().keys())
1669
1884
  return context
1670
1885
 
1671
1886
 
@@ -1674,6 +1889,27 @@ class PlatformUIViewSet(NautobotUIViewSet):
1674
1889
  #
1675
1890
 
1676
1891
 
1892
+ class DeviceBreadcrumbsMixin:
1893
+ breadcrumbs = Breadcrumbs(
1894
+ items={
1895
+ "detail": [
1896
+ ModelBreadcrumbItem(model=Device),
1897
+ ModelBreadcrumbItem(
1898
+ model=Device,
1899
+ reverse_query_params=lambda c: {"location": c["object"].location.pk},
1900
+ ),
1901
+ InstanceBreadcrumbItem(
1902
+ instance=lambda c: c["object"].parent_bay.device,
1903
+ should_render=lambda c: hasattr(c["object"], "parent_bay"),
1904
+ ),
1905
+ BaseBreadcrumbItem(
1906
+ label=lambda c: c["object"].parent_bay, should_render=lambda c: hasattr(c["object"], "parent_bay")
1907
+ ),
1908
+ ]
1909
+ }
1910
+ )
1911
+
1912
+
1677
1913
  class DeviceListView(generic.ObjectListView):
1678
1914
  queryset = Device.objects.select_related(
1679
1915
  "device_type__manufacturer", # Needed for __str__() on device_type
@@ -4127,16 +4363,32 @@ class PowerPanelUIViewSet(NautobotUIViewSet):
4127
4363
  "name",
4128
4364
  "location",
4129
4365
  "rack_group",
4366
+ "panel_type",
4367
+ "power_path",
4368
+ "breaker_position_count",
4130
4369
  ],
4131
4370
  ),
4132
4371
  object_detail.ObjectsTablePanel(
4133
4372
  section=SectionChoices.FULL_WIDTH,
4134
4373
  weight=100,
4135
4374
  table_class=tables.PowerFeedTable,
4375
+ table_filter="destination_panel",
4376
+ table_title="Incoming Feeders",
4377
+ exclude_columns=["destination_panel", "cable", "cable_peer", "rack"],
4378
+ add_button_route=None,
4379
+ paginate=False,
4380
+ show_table_config_button=False,
4381
+ ),
4382
+ object_detail.ObjectsTablePanel(
4383
+ section=SectionChoices.FULL_WIDTH,
4384
+ weight=200,
4385
+ table_class=tables.PowerFeedTable,
4136
4386
  table_filter="power_panel",
4137
4387
  table_title="Connected Feeds",
4138
4388
  exclude_columns=["power_panel"],
4139
4389
  add_button_route=None,
4390
+ paginate=False,
4391
+ show_table_config_button=False,
4140
4392
  ),
4141
4393
  )
4142
4394
  )
@@ -4188,13 +4440,22 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4188
4440
  section=SectionChoices.LEFT_HALF,
4189
4441
  weight=200,
4190
4442
  label="Electrical Characteristics",
4191
- fields=["supply", "voltage", "amperage", "phase", "max_utilization"],
4443
+ fields=["supply", "voltage", "amperage", "phase", "max_utilization", "available_power"],
4192
4444
  value_transforms={
4193
4445
  "voltage": [lambda v: f"{v}V" if v is not None else helpers.placeholder(v)],
4194
4446
  "amperage": [lambda a: f"{a}A" if a is not None else helpers.placeholder(a)],
4195
4447
  "max_utilization": [lambda p: f"{p}%" if p is not None else helpers.placeholder(p)],
4448
+ "available_power": [
4449
+ lambda p: f"{((p or 0) / 1000):.1f}kVA" if p is not None else helpers.placeholder(p)
4450
+ ],
4196
4451
  },
4197
4452
  ),
4453
+ object_detail.KeyValueTablePanel(
4454
+ section=SectionChoices.RIGHT_HALF,
4455
+ weight=200,
4456
+ label="Breaker Configuration",
4457
+ context_data_key="breaker_config_data",
4458
+ ),
4198
4459
  object_detail.KeyValueTablePanel(
4199
4460
  section=SectionChoices.RIGHT_HALF,
4200
4461
  weight=300,
@@ -4209,15 +4470,24 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4209
4470
  if not instance or self.action != "retrieve":
4210
4471
  return context
4211
4472
 
4473
+ label, value = self._get_connected_target(instance)
4212
4474
  context["powerfeed_data"] = {
4213
4475
  "Power Panel": instance.power_panel,
4214
4476
  "Rack": instance.rack,
4477
+ "Power Path": instance.get_power_path_display(),
4215
4478
  "Type": self._get_type_html(instance), # Render Type with HTML label
4216
4479
  "Status": instance.status,
4217
- "Connected Device": self._get_connected_device_html(instance),
4480
+ label: value,
4218
4481
  "Utilization (Allocated)": self._get_utilization_data(instance),
4219
4482
  }
4220
4483
 
4484
+ context["breaker_config_data"] = {
4485
+ "Breaker Position": instance.breaker_position,
4486
+ "Breaker Pole Count": instance.get_breaker_pole_count_display(),
4487
+ "Occupied Positions": instance.occupied_positions,
4488
+ "Phase Designation": instance.phase_designation,
4489
+ }
4490
+
4221
4491
  context["connection_data"] = self._get_connection_data(request, instance)
4222
4492
  return context
4223
4493
 
@@ -4229,6 +4499,12 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4229
4499
  type_class = instance.get_type_class()
4230
4500
  return format_html('<span class="label label-{}">{}</span>', type_class, instance.get_type_display())
4231
4501
 
4502
+ def _get_connected_target(self, instance):
4503
+ """Return (label, value) for the connected row."""
4504
+ if getattr(instance, "destination_panel", None):
4505
+ return "Connected Panel", helpers.hyperlinked_object(instance.destination_panel)
4506
+ return "Connected Device", self._get_connected_device_html(instance)
4507
+
4232
4508
  def _get_connected_device_html(self, instance):
4233
4509
  endpoint = getattr(instance, "connected_endpoint", None)
4234
4510
  if endpoint and endpoint.parent:
@@ -4251,6 +4527,12 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4251
4527
  if not instance:
4252
4528
  return {}
4253
4529
 
4530
+ if instance.destination_panel:
4531
+ return {
4532
+ "Power Panel": instance.destination_panel,
4533
+ "Panel Type": instance.destination_panel.get_panel_type_display(),
4534
+ }
4535
+
4254
4536
  if instance.cable:
4255
4537
  trace_url = reverse("dcim:powerfeed_trace", kwargs={"pk": instance.pk})
4256
4538
  cable_html = format_html(