nautobot 2.4.14__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 (433) 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 +9 -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/templatetags/custom_links.py +2 -2
  92. nautobot/extras/templatetags/job_buttons.py +1 -1
  93. nautobot/extras/templatetags/plugins.py +1 -1
  94. nautobot/extras/tests/integration/test_computedfields.py +2 -2
  95. nautobot/extras/tests/integration/test_customfields.py +14 -11
  96. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  97. nautobot/extras/tests/integration/test_notes.py +1 -1
  98. nautobot/extras/tests/integration/test_plugins.py +6 -6
  99. nautobot/extras/tests/integration/test_relationships.py +2 -2
  100. nautobot/extras/tests/test_filters.py +9 -0
  101. nautobot/extras/tests/test_forms.py +2 -2
  102. nautobot/extras/tests/test_plugins.py +2 -3
  103. nautobot/extras/tests/test_relationships.py +7 -7
  104. nautobot/extras/tests/test_views.py +172 -1
  105. nautobot/extras/urls.py +3 -59
  106. nautobot/extras/utils.py +1 -1
  107. nautobot/extras/views.py +77 -178
  108. nautobot/ipam/tables.py +8 -15
  109. nautobot/ipam/tests/migration/test_migrations.py +8 -8
  110. nautobot/ipam/tests/test_api.py +2 -2
  111. nautobot/ipam/tests/test_models.py +1 -1
  112. nautobot/project-static/docs/404.html +23 -0
  113. nautobot/project-static/docs/apps/index.html +23 -0
  114. nautobot/project-static/docs/apps/nautobot-apps.html +23 -0
  115. nautobot/project-static/docs/assets/_mkdocstrings.css +44 -6
  116. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +28 -0
  117. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +25 -0
  118. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +128 -20
  119. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +37 -4
  120. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +39 -6
  121. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +25 -0
  122. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +24 -0
  123. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +32 -5
  124. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +41 -8
  125. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +39 -7
  126. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +43 -10
  127. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +74 -59
  128. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +143 -28
  129. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +43 -12
  130. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +135 -53
  131. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +229 -36
  132. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +27 -1
  133. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -1
  134. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +162 -18
  135. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +258 -51
  136. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5987 -2620
  137. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +25 -0
  138. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +154 -55
  139. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +150 -35
  140. nautobot/project-static/docs/development/apps/api/configuration-view.html +23 -0
  141. nautobot/project-static/docs/development/apps/api/database-backend-config.html +23 -0
  142. nautobot/project-static/docs/development/apps/api/models/django-admin.html +23 -0
  143. nautobot/project-static/docs/development/apps/api/models/global-search.html +23 -0
  144. nautobot/project-static/docs/development/apps/api/models/graphql.html +23 -0
  145. nautobot/project-static/docs/development/apps/api/models/index.html +23 -0
  146. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +23 -0
  147. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +23 -0
  148. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +23 -0
  149. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +23 -0
  150. nautobot/project-static/docs/development/apps/api/platform-features/index.html +23 -0
  151. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +23 -0
  152. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +23 -0
  153. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +23 -0
  154. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +23 -0
  155. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +23 -0
  156. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +23 -0
  157. nautobot/project-static/docs/development/apps/api/prometheus.html +23 -0
  158. nautobot/project-static/docs/development/apps/api/setup.html +23 -0
  159. nautobot/project-static/docs/development/apps/api/testing.html +23 -0
  160. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +23 -0
  161. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +23 -0
  162. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +23 -0
  163. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +23 -0
  164. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +23 -0
  165. nautobot/project-static/docs/development/apps/api/views/base-template.html +23 -0
  166. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +23 -0
  167. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +23 -0
  168. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +23 -0
  169. nautobot/project-static/docs/development/apps/api/views/index.html +23 -0
  170. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +23 -0
  171. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -2
  172. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +23 -0
  173. nautobot/project-static/docs/development/apps/api/views/notes.html +23 -0
  174. nautobot/project-static/docs/development/apps/api/views/rest-api.html +23 -0
  175. nautobot/project-static/docs/development/apps/api/views/urls.html +23 -0
  176. nautobot/project-static/docs/development/apps/index.html +23 -0
  177. nautobot/project-static/docs/development/apps/migration/code-updates.html +23 -0
  178. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +23 -0
  179. nautobot/project-static/docs/development/apps/migration/from-v1.html +23 -0
  180. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +23 -0
  181. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +23 -0
  182. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +23 -0
  183. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +23 -0
  184. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +26 -3
  185. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +10544 -0
  186. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +23 -0
  187. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +23 -0
  188. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +23 -0
  189. nautobot/project-static/docs/development/apps/porting-from-netbox.html +26 -3
  190. nautobot/project-static/docs/development/core/application-registry.html +23 -0
  191. nautobot/project-static/docs/development/core/best-practices.html +23 -0
  192. nautobot/project-static/docs/development/core/bootstrap-ui.html +23 -0
  193. nautobot/project-static/docs/development/core/caching.html +23 -0
  194. nautobot/project-static/docs/development/core/controllers.html +23 -0
  195. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +23 -0
  196. nautobot/project-static/docs/development/core/generic-views.html +23 -0
  197. nautobot/project-static/docs/development/core/getting-started.html +23 -0
  198. nautobot/project-static/docs/development/core/homepage.html +23 -0
  199. nautobot/project-static/docs/development/core/index.html +23 -0
  200. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +23 -0
  201. nautobot/project-static/docs/development/core/model-checklist.html +23 -0
  202. nautobot/project-static/docs/development/core/model-features.html +23 -0
  203. nautobot/project-static/docs/development/core/natural-keys.html +23 -0
  204. nautobot/project-static/docs/development/core/navigation-menu.html +23 -0
  205. nautobot/project-static/docs/development/core/release-checklist.html +23 -0
  206. nautobot/project-static/docs/development/core/role-internals.html +23 -0
  207. nautobot/project-static/docs/development/core/settings.html +23 -0
  208. nautobot/project-static/docs/development/core/style-guide.html +23 -0
  209. nautobot/project-static/docs/development/core/templates.html +23 -0
  210. nautobot/project-static/docs/development/core/testing.html +23 -0
  211. nautobot/project-static/docs/development/core/ui-component-framework.html +713 -255
  212. nautobot/project-static/docs/development/core/user-preferences.html +23 -0
  213. nautobot/project-static/docs/development/index.html +23 -0
  214. nautobot/project-static/docs/development/jobs/getting-started.html +23 -0
  215. nautobot/project-static/docs/development/jobs/index.html +23 -0
  216. nautobot/project-static/docs/development/jobs/installation.html +23 -0
  217. nautobot/project-static/docs/development/jobs/job-extensions.html +23 -0
  218. nautobot/project-static/docs/development/jobs/job-logging.html +23 -0
  219. nautobot/project-static/docs/development/jobs/job-patterns.html +23 -0
  220. nautobot/project-static/docs/development/jobs/job-structure.html +23 -0
  221. nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -0
  222. nautobot/project-static/docs/development/jobs/testing.html +23 -0
  223. nautobot/project-static/docs/index.html +23 -0
  224. nautobot/project-static/docs/media/development/core/ui-component-framework/breadcrumbs-titles-data-flow.png +0 -0
  225. nautobot/project-static/docs/media/power_distribution.png +0 -0
  226. nautobot/project-static/docs/objects.inv +0 -0
  227. nautobot/project-static/docs/overview/application_stack.html +23 -0
  228. nautobot/project-static/docs/overview/design_philosophy.html +23 -0
  229. nautobot/project-static/docs/release-notes/index.html +23 -0
  230. nautobot/project-static/docs/release-notes/version-1.0.html +23 -0
  231. nautobot/project-static/docs/release-notes/version-1.1.html +23 -0
  232. nautobot/project-static/docs/release-notes/version-1.2.html +23 -0
  233. nautobot/project-static/docs/release-notes/version-1.3.html +23 -0
  234. nautobot/project-static/docs/release-notes/version-1.4.html +23 -0
  235. nautobot/project-static/docs/release-notes/version-1.5.html +23 -0
  236. nautobot/project-static/docs/release-notes/version-1.6.html +23 -0
  237. nautobot/project-static/docs/release-notes/version-2.0.html +23 -0
  238. nautobot/project-static/docs/release-notes/version-2.1.html +23 -0
  239. nautobot/project-static/docs/release-notes/version-2.2.html +23 -0
  240. nautobot/project-static/docs/release-notes/version-2.3.html +23 -0
  241. nautobot/project-static/docs/release-notes/version-2.4.html +231 -0
  242. nautobot/project-static/docs/requirements.txt +2 -2
  243. nautobot/project-static/docs/search/search_index.json +1 -1
  244. nautobot/project-static/docs/sitemap.xml +303 -299
  245. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  246. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +23 -0
  247. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +23 -0
  248. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +23 -0
  249. nautobot/project-static/docs/user-guide/administration/configuration/index.html +23 -0
  250. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +23 -0
  251. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +23 -0
  252. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +23 -0
  253. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +23 -0
  254. nautobot/project-static/docs/user-guide/administration/guides/docker.html +23 -0
  255. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +23 -0
  256. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +23 -0
  257. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +23 -0
  258. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +23 -0
  259. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +23 -0
  260. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +23 -0
  261. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +23 -0
  262. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +23 -0
  263. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +23 -0
  264. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +23 -0
  265. nautobot/project-static/docs/user-guide/administration/installation/index.html +23 -0
  266. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +23 -0
  267. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +23 -0
  268. nautobot/project-static/docs/user-guide/administration/installation/services.html +23 -0
  269. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +23 -0
  270. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +23 -0
  271. nautobot/project-static/docs/user-guide/administration/security/index.html +23 -0
  272. nautobot/project-static/docs/user-guide/administration/security/notices.html +23 -0
  273. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +284 -219
  274. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +23 -0
  275. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +23 -0
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +23 -0
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +23 -0
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +23 -0
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +23 -0
  280. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +23 -0
  281. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +23 -0
  282. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +23 -0
  283. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +23 -0
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +23 -0
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +23 -0
  286. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +23 -0
  287. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +23 -0
  288. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +23 -0
  289. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +23 -0
  290. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +23 -0
  291. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +23 -0
  292. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +23 -0
  293. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +23 -0
  294. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +23 -0
  295. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +23 -0
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +23 -0
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +23 -0
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +23 -0
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +23 -0
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +23 -0
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +23 -0
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +23 -0
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +23 -0
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +23 -0
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +23 -0
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +23 -0
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +23 -0
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +23 -0
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +23 -0
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +23 -0
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +23 -0
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +23 -0
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +23 -0
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +23 -0
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +23 -0
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +23 -0
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +23 -0
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +23 -0
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +23 -0
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +23 -0
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +23 -0
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +23 -0
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +23 -0
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +305 -5
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +24 -1
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +23 -0
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +136 -3
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +41 -1
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +40 -0
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +23 -0
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +23 -0
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +23 -0
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +23 -0
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +23 -0
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +23 -0
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +23 -0
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +23 -0
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +23 -0
  339. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +23 -0
  340. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +23 -0
  341. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +23 -0
  342. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +23 -0
  343. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +23 -0
  344. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +23 -0
  345. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +23 -0
  346. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +23 -0
  347. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +23 -0
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +23 -0
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +23 -0
  350. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +23 -0
  351. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +23 -0
  352. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +23 -0
  353. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +23 -0
  354. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +23 -0
  355. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +23 -0
  356. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +23 -0
  357. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +23 -0
  358. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +23 -0
  359. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +23 -0
  360. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +23 -0
  361. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +23 -0
  362. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +23 -0
  363. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +23 -0
  364. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +23 -0
  365. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +23 -0
  366. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +23 -0
  367. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +23 -0
  368. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +23 -0
  369. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +23 -0
  370. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +23 -0
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +23 -0
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +23 -0
  373. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +23 -0
  374. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +23 -0
  375. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +23 -0
  376. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +23 -0
  377. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +23 -0
  378. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +23 -0
  379. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +23 -0
  380. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +23 -0
  381. nautobot/project-static/docs/user-guide/index.html +23 -0
  382. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +23 -0
  383. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +23 -0
  384. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +23 -0
  385. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +23 -0
  386. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +23 -0
  387. nautobot/project-static/docs/user-guide/platform-functionality/events.html +23 -0
  388. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +23 -0
  389. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +23 -0
  390. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +23 -0
  391. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +23 -0
  392. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +23 -0
  393. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +23 -0
  394. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +23 -0
  395. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +24 -1
  396. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +23 -0
  397. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +23 -0
  398. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +23 -0
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +23 -0
  400. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +23 -0
  401. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +23 -0
  402. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +23 -0
  403. nautobot/project-static/docs/user-guide/platform-functionality/note.html +23 -0
  404. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +23 -0
  405. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +23 -0
  406. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +23 -0
  407. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +23 -0
  408. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +23 -0
  409. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +23 -0
  410. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +23 -0
  411. nautobot/project-static/docs/user-guide/platform-functionality/role.html +23 -0
  412. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +23 -0
  413. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +23 -0
  414. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +23 -0
  415. nautobot/project-static/docs/user-guide/platform-functionality/status.html +23 -0
  416. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +23 -0
  417. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +23 -0
  418. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +23 -0
  419. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +23 -0
  420. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +23 -0
  421. nautobot/users/tests/test_api.py +2 -2
  422. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -252
  423. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -75
  424. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +252 -0
  425. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +75 -0
  426. nautobot/virtualization/urls.py +3 -61
  427. nautobot/virtualization/views.py +48 -72
  428. {nautobot-2.4.14.dist-info → nautobot-2.4.15.dist-info}/METADATA +24 -24
  429. {nautobot-2.4.14.dist-info → nautobot-2.4.15.dist-info}/RECORD +433 -416
  430. {nautobot-2.4.14.dist-info → nautobot-2.4.15.dist-info}/LICENSE.txt +0 -0
  431. {nautobot-2.4.14.dist-info → nautobot-2.4.15.dist-info}/NOTICE +0 -0
  432. {nautobot-2.4.14.dist-info → nautobot-2.4.15.dist-info}/WHEEL +0 -0
  433. {nautobot-2.4.14.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,6 +66,7 @@ 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
@@ -257,10 +269,11 @@ class LocationTypeUIViewSet(NautobotUIViewSet):
257
269
 
258
270
 
259
271
  class LocationUIViewSet(NautobotUIViewSet):
260
- # We aren't accessing tree fields anywhere so this is safe (note that `parent` itself is a normal foreign
261
- # 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
262
275
  # automatically issue a second query (similar to behavior for
263
- # 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).
264
277
  queryset = Location.objects.without_tree_fields().select_related("location_type", "parent", "tenant")
265
278
  filterset_class = filters.LocationFilterSet
266
279
  filterset_form_class = forms.LocationFilterForm
@@ -725,6 +738,173 @@ class ManufacturerUIViewSet(NautobotUIViewSet):
725
738
  #
726
739
  # Device types
727
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 ---
728
908
  class DeviceTypeUIViewSet(NautobotUIViewSet):
729
909
  bulk_update_form_class = forms.DeviceTypeBulkEditForm
730
910
  filterset_class = filters.DeviceTypeFilterSet
@@ -734,81 +914,147 @@ class DeviceTypeUIViewSet(NautobotUIViewSet):
734
914
  table_class = tables.DeviceTypeTable
735
915
  queryset = DeviceType.objects.select_related("manufacturer").prefetch_related("software_image_files")
736
916
 
737
- def get_extra_context(self, request, instance):
738
- if self.action != "retrieve":
739
- return {}
740
- 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
+ )
741
967
 
742
- # Component tables
743
- consoleport_table = tables.ConsolePortTemplateTable(
744
- ConsolePortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
745
- orderable=False,
746
- )
747
- consoleserverport_table = tables.ConsoleServerPortTemplateTable(
748
- ConsoleServerPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
749
- orderable=False,
750
- )
751
- powerport_table = tables.PowerPortTemplateTable(
752
- PowerPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
753
- orderable=False,
754
- )
755
- poweroutlet_table = tables.PowerOutletTemplateTable(
756
- PowerOutletTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
757
- orderable=False,
758
- )
759
- interface_table = tables.InterfaceTemplateTable(
760
- list(InterfaceTemplate.objects.restrict(request.user, "view").filter(device_type=instance)),
761
- orderable=False,
762
- )
763
- front_port_table = tables.FrontPortTemplateTable(
764
- FrontPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
765
- orderable=False,
766
- )
767
- rear_port_table = tables.RearPortTemplateTable(
768
- RearPortTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
769
- orderable=False,
770
- )
771
- devicebay_table = tables.DeviceBayTemplateTable(
772
- DeviceBayTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
773
- orderable=False,
774
- )
775
- modulebay_table = tables.ModuleBayTemplateTable(
776
- ModuleBayTemplate.objects.restrict(request.user, "view").filter(device_type=instance),
777
- orderable=False,
778
- )
779
- if request.user.has_perm("dcim.change_devicetype"):
780
- consoleport_table.columns.show("pk")
781
- consoleserverport_table.columns.show("pk")
782
- powerport_table.columns.show("pk")
783
- poweroutlet_table.columns.show("pk")
784
- interface_table.columns.show("pk")
785
- front_port_table.columns.show("pk")
786
- rear_port_table.columns.show("pk")
787
- devicebay_table.columns.show("pk")
788
- 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({})
789
978
 
790
- software_image_files_table = tables.SoftwareImageFileTable(
791
- instance.software_image_files.restrict(request.user, "view").annotate(
792
- device_type_count=count_related(DeviceType, "software_image_files"),
793
- ),
794
- orderable=False,
795
- exclude=["actions", "tags"],
796
- )
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({})
797
988
 
798
- return {
799
- "instance_count": instance_count,
800
- "consoleport_table": consoleport_table,
801
- "consoleserverport_table": consoleserverport_table,
802
- "powerport_table": powerport_table,
803
- "poweroutlet_table": poweroutlet_table,
804
- "interface_table": interface_table,
805
- "front_port_table": front_port_table,
806
- "rear_port_table": rear_port_table,
807
- "devicebay_table": devicebay_table,
808
- "modulebay_table": modulebay_table,
809
- "software_image_files_table": software_image_files_table,
810
- **super().get_extra_context(request, instance),
811
- }
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({})
812
1058
 
813
1059
 
814
1060
  class DeviceTypeImportView(generic.ObjectImportView):
@@ -1643,6 +1889,27 @@ class PlatformUIViewSet(NautobotUIViewSet):
1643
1889
  #
1644
1890
 
1645
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
+
1646
1913
  class DeviceListView(generic.ObjectListView):
1647
1914
  queryset = Device.objects.select_related(
1648
1915
  "device_type__manufacturer", # Needed for __str__() on device_type
@@ -4096,16 +4363,32 @@ class PowerPanelUIViewSet(NautobotUIViewSet):
4096
4363
  "name",
4097
4364
  "location",
4098
4365
  "rack_group",
4366
+ "panel_type",
4367
+ "power_path",
4368
+ "breaker_position_count",
4099
4369
  ],
4100
4370
  ),
4101
4371
  object_detail.ObjectsTablePanel(
4102
4372
  section=SectionChoices.FULL_WIDTH,
4103
4373
  weight=100,
4104
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,
4105
4386
  table_filter="power_panel",
4106
4387
  table_title="Connected Feeds",
4107
4388
  exclude_columns=["power_panel"],
4108
4389
  add_button_route=None,
4390
+ paginate=False,
4391
+ show_table_config_button=False,
4109
4392
  ),
4110
4393
  )
4111
4394
  )
@@ -4157,13 +4440,22 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4157
4440
  section=SectionChoices.LEFT_HALF,
4158
4441
  weight=200,
4159
4442
  label="Electrical Characteristics",
4160
- fields=["supply", "voltage", "amperage", "phase", "max_utilization"],
4443
+ fields=["supply", "voltage", "amperage", "phase", "max_utilization", "available_power"],
4161
4444
  value_transforms={
4162
4445
  "voltage": [lambda v: f"{v}V" if v is not None else helpers.placeholder(v)],
4163
4446
  "amperage": [lambda a: f"{a}A" if a is not None else helpers.placeholder(a)],
4164
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
+ ],
4165
4451
  },
4166
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
+ ),
4167
4459
  object_detail.KeyValueTablePanel(
4168
4460
  section=SectionChoices.RIGHT_HALF,
4169
4461
  weight=300,
@@ -4178,15 +4470,24 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4178
4470
  if not instance or self.action != "retrieve":
4179
4471
  return context
4180
4472
 
4473
+ label, value = self._get_connected_target(instance)
4181
4474
  context["powerfeed_data"] = {
4182
4475
  "Power Panel": instance.power_panel,
4183
4476
  "Rack": instance.rack,
4477
+ "Power Path": instance.get_power_path_display(),
4184
4478
  "Type": self._get_type_html(instance), # Render Type with HTML label
4185
4479
  "Status": instance.status,
4186
- "Connected Device": self._get_connected_device_html(instance),
4480
+ label: value,
4187
4481
  "Utilization (Allocated)": self._get_utilization_data(instance),
4188
4482
  }
4189
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
+
4190
4491
  context["connection_data"] = self._get_connection_data(request, instance)
4191
4492
  return context
4192
4493
 
@@ -4198,6 +4499,12 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4198
4499
  type_class = instance.get_type_class()
4199
4500
  return format_html('<span class="label label-{}">{}</span>', type_class, instance.get_type_display())
4200
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
+
4201
4508
  def _get_connected_device_html(self, instance):
4202
4509
  endpoint = getattr(instance, "connected_endpoint", None)
4203
4510
  if endpoint and endpoint.parent:
@@ -4220,6 +4527,12 @@ class PowerFeedUIViewSet(NautobotUIViewSet):
4220
4527
  if not instance:
4221
4528
  return {}
4222
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
+
4223
4536
  if instance.cable:
4224
4537
  trace_url = reverse("dcim:powerfeed_trace", kwargs={"pk": instance.pk})
4225
4538
  cable_html = format_html(
@@ -1,4 +1,4 @@
1
- from datetime import timedelta, timezone
1
+ from datetime import timezone
2
2
  import json
3
3
 
4
4
  from django.contrib.auth import get_user_model
@@ -137,8 +137,10 @@ class JobLogEntryFactory(BaseModelFactory):
137
137
  def created(self):
138
138
  if self.job_result.date_done:
139
139
  return faker.Faker().date_time_between_dates(
140
- datetime_start=self.job_result.date_created, datetime_end=self.job_result.date_done, tzinfo=timezone.utc
140
+ datetime_start=self.job_result.date_started, datetime_end=self.job_result.date_done, tzinfo=timezone.utc
141
141
  )
142
+ elif self.job_result.date_started:
143
+ return faker.Faker().past_datetime(start_date=self.job_result.date_started, tzinfo=timezone.utc)
142
144
  return faker.Faker().past_datetime(start_date=self.job_result.date_created, tzinfo=timezone.utc)
143
145
 
144
146
 
@@ -183,7 +185,7 @@ class JobResultFactory(BaseModelFactory):
183
185
  job_model = factory.Maybe("has_job_model", random_instance(Job), None)
184
186
  name = factory.Faker("word")
185
187
  task_name = factory.Faker("word")
186
- # date_created and date_done are handled below
188
+ # date_created, date_started, and date_done are handled below
187
189
  user = factory.Maybe("has_user", random_instance(get_user_model()), None)
188
190
  status = factory.Iterator(
189
191
  [
@@ -210,23 +212,20 @@ class JobResultFactory(BaseModelFactory):
210
212
  return None
211
213
 
212
214
  @factory.post_generation
213
- def date_created(self, created, extracted, **kwargs): # pylint: disable=method-hidden
214
- if created:
215
- if extracted:
216
- self.date_created = extracted
217
- else:
218
- self.date_created = faker.Faker().date_time_between(
219
- start_date="-1y", end_date="now", tzinfo=timezone.utc
220
- )
221
-
222
- @factory.post_generation
223
- def date_done(self, created, extracted, **kwargs): # pylint: disable=method-hidden
224
- if created:
225
- if extracted:
226
- self.date_done = extracted
227
- else:
228
- # TODO, should we create "in progress" job results without a date_done value as well?
229
- self.date_done = self.date_created + timedelta(minutes=faker.Faker().random_int())
215
+ def dates(self, created, extracted, **kwargs): # pylint: disable=method-hidden
216
+ if not created:
217
+ return
218
+ if extracted:
219
+ return
220
+ # Create a date_created in the past, but not too far in the past
221
+ self.date_created = faker.Faker().date_time_between(start_date="-1y", end_date="-1w", tzinfo=timezone.utc)
222
+ self.date_started = faker.Faker().date_time_between(
223
+ start_date=self.date_created, end_date="-1d", tzinfo=timezone.utc
224
+ )
225
+ # TODO, should we create "in progress" job results without a date_done value as well?
226
+ self.date_done = faker.Faker().date_time_between(
227
+ start_date=self.date_started, end_date="now", tzinfo=timezone.utc
228
+ )
230
229
 
231
230
 
232
231
  class MetadataChoiceFactory(BaseModelFactory):
@@ -1,4 +1,5 @@
1
1
  from difflib import get_close_matches
2
+ from uuid import UUID
2
3
 
3
4
  from django.conf import settings
4
5
  from django.contrib.auth import get_user_model
@@ -1012,7 +1013,7 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
1012
1013
 
1013
1014
  class Meta:
1014
1015
  model = JobResult
1015
- fields = ["id", "date_created", "date_done", "name", "status", "user", "scheduled_job"]
1016
+ fields = ["id", "date_created", "date_started", "date_done", "name", "status", "user", "scheduled_job"]
1016
1017
 
1017
1018
 
1018
1019
  class JobLogEntryFilterSet(BaseFilterSet):
@@ -1193,7 +1194,7 @@ class NoteFilterSet(BaseFilterSet):
1193
1194
  filter_predicates={
1194
1195
  "user_name": "icontains",
1195
1196
  "note": "icontains",
1196
- "assigned_object_id": "exact",
1197
+ "assigned_object_id": {"lookup_expr": "exact", "preprocessor": UUID},
1197
1198
  },
1198
1199
  )
1199
1200
  assigned_object_type = ContentTypeFilter()