nautobot 2.4.10__py3-none-any.whl → 2.4.12__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (439) hide show
  1. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -114
  2. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +1 -1
  3. nautobot/circuits/views.py +76 -6
  4. nautobot/cloud/navigation.py +1 -1
  5. nautobot/cloud/tests/test_views.py +13 -1
  6. nautobot/cloud/views.py +39 -9
  7. nautobot/core/celery/__init__.py +21 -0
  8. nautobot/core/celery/encoders.py +3 -0
  9. nautobot/core/forms/forms.py +4 -1
  10. nautobot/core/jobs/bulk_actions.py +8 -8
  11. nautobot/core/jobs/cleanup.py +11 -0
  12. nautobot/core/management/commands/generate_test_data.py +2 -1
  13. nautobot/core/templates/components/panel/header_extra_content_table.html +12 -1
  14. nautobot/core/templates/components/panel/panel.html +4 -0
  15. nautobot/core/templates/generic/object_retrieve.html +2 -1
  16. nautobot/core/testing/mixins.py +19 -1
  17. nautobot/core/testing/views.py +104 -8
  18. nautobot/core/tests/test_jobs.py +20 -4
  19. nautobot/core/tests/test_utils.py +17 -0
  20. nautobot/core/tests/test_views.py +2 -2
  21. nautobot/core/tests/test_views_utils.py +53 -2
  22. nautobot/core/ui/object_detail.py +5 -1
  23. nautobot/core/utils/lookup.py +4 -2
  24. nautobot/core/utils/module_loading.py +23 -0
  25. nautobot/core/views/generic.py +2 -12
  26. nautobot/core/views/mixins.py +19 -1
  27. nautobot/core/views/renderers.py +4 -13
  28. nautobot/core/views/utils.py +16 -0
  29. nautobot/dcim/api/serializers.py +13 -0
  30. nautobot/dcim/api/urls.py +1 -0
  31. nautobot/dcim/api/views.py +20 -0
  32. nautobot/dcim/apps.py +1 -0
  33. nautobot/dcim/factory.py +11 -0
  34. nautobot/dcim/filters/__init__.py +116 -0
  35. nautobot/dcim/filters/mixins.py +2 -1
  36. nautobot/dcim/forms.py +205 -19
  37. nautobot/dcim/migrations/0070_modulefamily_models.py +92 -0
  38. nautobot/dcim/models/__init__.py +2 -0
  39. nautobot/dcim/models/device_component_templates.py +14 -0
  40. nautobot/dcim/models/device_components.py +13 -1
  41. nautobot/dcim/models/devices.py +72 -0
  42. nautobot/dcim/navigation.py +16 -0
  43. nautobot/dcim/tables/__init__.py +2 -0
  44. nautobot/dcim/tables/devices.py +50 -0
  45. nautobot/dcim/tables/devicetypes.py +35 -1
  46. nautobot/dcim/tables/template_code.py +2 -0
  47. nautobot/dcim/templates/dcim/controller/base.html +1 -9
  48. nautobot/dcim/templates/dcim/controller_retrieve.html +2 -83
  49. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +2 -25
  50. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -90
  51. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
  52. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +1 -63
  53. nautobot/dcim/templates/dcim/location.html +2 -249
  54. nautobot/dcim/templates/dcim/location_edit.html +2 -38
  55. nautobot/dcim/templates/dcim/location_retrieve.html +249 -0
  56. nautobot/dcim/templates/dcim/location_update.html +38 -0
  57. nautobot/dcim/templates/dcim/module_update.html +1 -0
  58. nautobot/dcim/templates/dcim/modulebay_retrieve.html +93 -1
  59. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +31 -0
  60. nautobot/dcim/templates/dcim/moduletype_retrieve.html +6 -0
  61. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -160
  62. nautobot/dcim/templates/dcim/virtualchassis.html +2 -51
  63. nautobot/dcim/templates/dcim/virtualchassis_add.html +2 -22
  64. nautobot/dcim/templates/dcim/virtualchassis_create.html +22 -0
  65. nautobot/dcim/templates/dcim/virtualchassis_edit.html +2 -93
  66. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +51 -0
  67. nautobot/dcim/templates/dcim/virtualchassis_update.html +93 -0
  68. nautobot/dcim/tests/test_api.py +35 -0
  69. nautobot/dcim/tests/test_filters.py +102 -3
  70. nautobot/dcim/tests/test_models.py +146 -0
  71. nautobot/dcim/tests/test_views.py +70 -97
  72. nautobot/dcim/urls.py +5 -80
  73. nautobot/dcim/views.py +584 -342
  74. nautobot/extras/api/views.py +9 -2
  75. nautobot/extras/datasources/git.py +9 -1
  76. nautobot/extras/forms/forms.py +9 -5
  77. nautobot/extras/jobs.py +4 -2
  78. nautobot/extras/jobs_ui.py +208 -0
  79. nautobot/extras/models/datasources.py +5 -8
  80. nautobot/extras/models/jobs.py +5 -0
  81. nautobot/extras/models/tags.py +4 -0
  82. nautobot/extras/plugins/__init__.py +3 -0
  83. nautobot/extras/tables.py +40 -3
  84. nautobot/extras/templates/extras/configcontext.html +2 -220
  85. nautobot/extras/templates/extras/configcontext_edit.html +2 -50
  86. nautobot/extras/templates/extras/configcontext_retrieve.html +2 -0
  87. nautobot/extras/templates/extras/configcontext_update.html +50 -0
  88. nautobot/extras/templates/extras/configcontextschema.html +2 -48
  89. nautobot/extras/templates/extras/configcontextschema_edit.html +2 -19
  90. nautobot/extras/templates/extras/configcontextschema_retrieve.html +48 -0
  91. nautobot/extras/templates/extras/configcontextschema_update.html +19 -0
  92. nautobot/extras/templates/extras/inc/configcontext_data.html +1 -0
  93. nautobot/extras/templates/extras/inc/configcontext_format.html +6 -0
  94. nautobot/extras/templates/extras/job_detail.html +1 -326
  95. nautobot/extras/templates/extras/job_edit.html +12 -6
  96. nautobot/extras/templates/extras/tag.html +2 -52
  97. nautobot/extras/templates/extras/tag_edit.html +2 -15
  98. nautobot/extras/templates/extras/tag_retrieve.html +2 -0
  99. nautobot/extras/templates/extras/tag_update.html +15 -0
  100. nautobot/extras/templates/extras/team_retrieve.html +2 -2
  101. nautobot/extras/tests/test_api.py +15 -15
  102. nautobot/extras/tests/test_filters.py +4 -4
  103. nautobot/extras/tests/test_jobs.py +23 -10
  104. nautobot/extras/tests/test_models.py +19 -8
  105. nautobot/extras/tests/test_plugins.py +6 -3
  106. nautobot/extras/tests/test_views.py +66 -11
  107. nautobot/extras/urls.py +4 -134
  108. nautobot/extras/views.py +186 -178
  109. nautobot/ipam/models.py +19 -4
  110. nautobot/ipam/tables.py +19 -0
  111. nautobot/ipam/templates/ipam/vlan.html +2 -84
  112. nautobot/ipam/templates/ipam/vlan_edit.html +2 -24
  113. nautobot/ipam/templates/ipam/vlan_retrieve.html +84 -0
  114. nautobot/ipam/templates/ipam/vlan_update.html +24 -0
  115. nautobot/ipam/tests/test_views.py +5 -0
  116. nautobot/ipam/urls.py +1 -21
  117. nautobot/ipam/views.py +45 -70
  118. nautobot/project-static/docs/404.html +33 -10
  119. nautobot/project-static/docs/apps/index.html +33 -10
  120. nautobot/project-static/docs/apps/nautobot-apps.html +33 -10
  121. nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js → bundle.56ea9cef.min.js} +2 -2
  122. nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js.map → bundle.56ea9cef.min.js.map} +2 -2
  123. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +33 -10
  124. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +33 -10
  125. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +33 -10
  126. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +33 -10
  127. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +33 -10
  128. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +33 -10
  129. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +33 -10
  130. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +33 -10
  131. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +33 -10
  132. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +33 -10
  133. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +33 -10
  134. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +33 -10
  135. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +33 -10
  136. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +33 -10
  137. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +33 -10
  138. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +33 -10
  139. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +33 -10
  140. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +33 -10
  141. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +33 -10
  142. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +122 -10
  143. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +33 -10
  144. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +33 -10
  145. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +33 -10
  146. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +33 -10
  147. nautobot/project-static/docs/development/apps/api/configuration-view.html +33 -10
  148. nautobot/project-static/docs/development/apps/api/database-backend-config.html +33 -10
  149. nautobot/project-static/docs/development/apps/api/models/django-admin.html +33 -10
  150. nautobot/project-static/docs/development/apps/api/models/global-search.html +33 -10
  151. nautobot/project-static/docs/development/apps/api/models/graphql.html +33 -10
  152. nautobot/project-static/docs/development/apps/api/models/index.html +33 -10
  153. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +42 -10
  154. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +33 -10
  155. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +33 -10
  156. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +33 -10
  157. nautobot/project-static/docs/development/apps/api/platform-features/index.html +33 -10
  158. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +33 -10
  159. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +33 -10
  160. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +33 -10
  161. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +34 -11
  162. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +33 -10
  163. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +33 -10
  164. nautobot/project-static/docs/development/apps/api/prometheus.html +33 -10
  165. nautobot/project-static/docs/development/apps/api/setup.html +33 -10
  166. nautobot/project-static/docs/development/apps/api/testing.html +33 -10
  167. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +33 -10
  168. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +33 -10
  169. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +33 -10
  170. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +33 -10
  171. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +33 -10
  172. nautobot/project-static/docs/development/apps/api/views/base-template.html +33 -10
  173. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +33 -10
  174. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +33 -10
  175. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +33 -10
  176. nautobot/project-static/docs/development/apps/api/views/index.html +33 -10
  177. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +33 -10
  178. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +33 -10
  179. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +33 -10
  180. nautobot/project-static/docs/development/apps/api/views/notes.html +33 -10
  181. nautobot/project-static/docs/development/apps/api/views/rest-api.html +33 -10
  182. nautobot/project-static/docs/development/apps/api/views/urls.html +33 -10
  183. nautobot/project-static/docs/development/apps/index.html +33 -10
  184. nautobot/project-static/docs/development/apps/migration/code-updates.html +33 -10
  185. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +33 -10
  186. nautobot/project-static/docs/development/apps/migration/from-v1.html +33 -10
  187. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +33 -10
  188. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +33 -10
  189. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +33 -10
  190. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +33 -10
  191. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +33 -10
  192. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +33 -10
  193. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +33 -10
  194. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +33 -10
  195. nautobot/project-static/docs/development/apps/porting-from-netbox.html +33 -10
  196. nautobot/project-static/docs/development/core/application-registry.html +33 -10
  197. nautobot/project-static/docs/development/core/best-practices.html +33 -10
  198. nautobot/project-static/docs/development/core/bootstrap-ui.html +33 -10
  199. nautobot/project-static/docs/development/core/caching.html +33 -10
  200. nautobot/project-static/docs/development/core/controllers.html +33 -10
  201. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +33 -10
  202. nautobot/project-static/docs/development/core/generic-views.html +33 -10
  203. nautobot/project-static/docs/development/core/getting-started.html +33 -10
  204. nautobot/project-static/docs/development/core/homepage.html +33 -10
  205. nautobot/project-static/docs/development/core/index.html +33 -10
  206. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +33 -10
  207. nautobot/project-static/docs/development/core/model-checklist.html +33 -10
  208. nautobot/project-static/docs/development/core/model-features.html +33 -10
  209. nautobot/project-static/docs/development/core/natural-keys.html +33 -10
  210. nautobot/project-static/docs/development/core/navigation-menu.html +33 -10
  211. nautobot/project-static/docs/development/core/release-checklist.html +33 -10
  212. nautobot/project-static/docs/development/core/role-internals.html +33 -10
  213. nautobot/project-static/docs/development/core/settings.html +33 -10
  214. nautobot/project-static/docs/development/core/style-guide.html +33 -10
  215. nautobot/project-static/docs/development/core/templates.html +33 -10
  216. nautobot/project-static/docs/development/core/testing.html +33 -10
  217. nautobot/project-static/docs/development/core/ui-component-framework.html +33 -10
  218. nautobot/project-static/docs/development/core/user-preferences.html +33 -10
  219. nautobot/project-static/docs/development/index.html +33 -10
  220. nautobot/project-static/docs/development/jobs/getting-started.html +33 -10
  221. nautobot/project-static/docs/development/jobs/index.html +33 -10
  222. nautobot/project-static/docs/development/jobs/installation.html +33 -10
  223. nautobot/project-static/docs/development/jobs/job-extensions.html +33 -10
  224. nautobot/project-static/docs/development/jobs/job-logging.html +33 -10
  225. nautobot/project-static/docs/development/jobs/job-patterns.html +33 -10
  226. nautobot/project-static/docs/development/jobs/job-structure.html +33 -10
  227. nautobot/project-static/docs/development/jobs/migration/from-v1.html +33 -10
  228. nautobot/project-static/docs/development/jobs/testing.html +33 -10
  229. nautobot/project-static/docs/index.html +33 -10
  230. nautobot/project-static/docs/insert-analytics.sh +36 -0
  231. nautobot/project-static/docs/objects.inv +0 -0
  232. nautobot/project-static/docs/overview/application_stack.html +33 -10
  233. nautobot/project-static/docs/overview/design_philosophy.html +33 -10
  234. nautobot/project-static/docs/release-notes/index.html +33 -10
  235. nautobot/project-static/docs/release-notes/version-1.0.html +33 -10
  236. nautobot/project-static/docs/release-notes/version-1.1.html +33 -10
  237. nautobot/project-static/docs/release-notes/version-1.2.html +33 -10
  238. nautobot/project-static/docs/release-notes/version-1.3.html +33 -10
  239. nautobot/project-static/docs/release-notes/version-1.4.html +33 -10
  240. nautobot/project-static/docs/release-notes/version-1.5.html +33 -10
  241. nautobot/project-static/docs/release-notes/version-1.6.html +33 -10
  242. nautobot/project-static/docs/release-notes/version-2.0.html +33 -10
  243. nautobot/project-static/docs/release-notes/version-2.1.html +33 -10
  244. nautobot/project-static/docs/release-notes/version-2.2.html +33 -10
  245. nautobot/project-static/docs/release-notes/version-2.3.html +33 -10
  246. nautobot/project-static/docs/release-notes/version-2.4.html +363 -10
  247. nautobot/project-static/docs/requirements.txt +1 -1
  248. nautobot/project-static/docs/search/search_index.json +1 -1
  249. nautobot/project-static/docs/sitemap.xml +302 -298
  250. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  251. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +33 -10
  252. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +33 -10
  253. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +33 -10
  254. nautobot/project-static/docs/user-guide/administration/configuration/index.html +33 -10
  255. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +33 -10
  256. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +33 -10
  257. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +33 -10
  258. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +33 -10
  259. nautobot/project-static/docs/user-guide/administration/guides/docker.html +33 -10
  260. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +33 -10
  261. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +33 -10
  262. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +33 -10
  263. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +33 -10
  264. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +33 -10
  265. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +33 -10
  266. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +33 -10
  267. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +33 -10
  268. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +33 -10
  269. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +33 -10
  270. nautobot/project-static/docs/user-guide/administration/installation/index.html +33 -10
  271. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +33 -10
  272. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +33 -10
  273. nautobot/project-static/docs/user-guide/administration/installation/services.html +33 -10
  274. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +33 -10
  275. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +33 -10
  276. nautobot/project-static/docs/user-guide/administration/security/index.html +33 -10
  277. nautobot/project-static/docs/user-guide/administration/security/notices.html +33 -10
  278. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +33 -10
  279. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +33 -10
  280. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +33 -10
  281. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +33 -10
  282. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +33 -10
  283. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +33 -10
  284. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +33 -10
  285. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +33 -10
  286. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +33 -10
  287. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +33 -10
  288. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +33 -10
  289. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +33 -10
  290. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +33 -10
  291. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +33 -10
  292. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +33 -10
  293. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +33 -10
  294. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +33 -10
  295. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +33 -10
  296. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +33 -10
  297. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +33 -10
  298. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +33 -10
  299. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +33 -10
  300. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +33 -10
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +33 -10
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +33 -10
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +33 -10
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +33 -10
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +33 -10
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +33 -10
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +33 -10
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +33 -10
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +33 -10
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +33 -10
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +33 -10
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +45 -22
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +33 -10
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +33 -10
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +33 -10
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +33 -10
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +33 -10
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +33 -10
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +33 -10
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +33 -10
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +33 -10
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +33 -10
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +37 -10
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +37 -10
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +37 -10
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +10261 -0
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +36 -13
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +33 -10
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +33 -10
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +33 -10
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +33 -10
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +33 -10
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +33 -10
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +33 -10
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +33 -10
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +33 -10
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +33 -10
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +33 -10
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +33 -10
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +33 -10
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +33 -10
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +33 -10
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +33 -10
  344. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +33 -10
  345. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +33 -10
  346. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +33 -10
  347. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +33 -10
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +33 -10
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +33 -10
  350. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +33 -10
  351. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +33 -10
  352. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +33 -10
  353. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +33 -10
  354. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +33 -10
  355. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +33 -10
  356. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +33 -10
  357. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +33 -10
  358. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +33 -10
  359. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +33 -10
  360. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +33 -10
  361. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +33 -10
  362. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +33 -10
  363. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +33 -10
  364. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +33 -10
  365. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +33 -10
  366. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +33 -10
  367. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +33 -10
  368. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +33 -10
  369. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +33 -10
  370. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +33 -10
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +33 -10
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +33 -10
  373. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +33 -10
  374. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +33 -10
  375. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +33 -10
  376. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +33 -10
  377. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +33 -10
  378. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +33 -10
  379. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +33 -10
  380. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +44 -18
  381. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +33 -10
  382. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +33 -10
  383. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +33 -10
  384. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +33 -10
  385. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +33 -10
  386. nautobot/project-static/docs/user-guide/index.html +33 -10
  387. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +33 -10
  388. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +33 -10
  389. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +33 -10
  390. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +33 -10
  391. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +33 -10
  392. nautobot/project-static/docs/user-guide/platform-functionality/events.html +33 -10
  393. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +33 -10
  394. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +33 -10
  395. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +39 -11
  396. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +33 -10
  397. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +33 -10
  398. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +33 -10
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +33 -10
  400. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +33 -10
  401. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +33 -10
  402. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +33 -10
  403. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +33 -10
  404. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +33 -10
  405. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +33 -10
  406. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +33 -10
  407. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +33 -10
  408. nautobot/project-static/docs/user-guide/platform-functionality/note.html +33 -10
  409. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +33 -10
  410. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +33 -10
  411. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +33 -10
  412. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +33 -10
  413. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +33 -10
  414. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +33 -10
  415. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +33 -10
  416. nautobot/project-static/docs/user-guide/platform-functionality/role.html +33 -10
  417. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +33 -10
  418. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +33 -10
  419. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +33 -10
  420. nautobot/project-static/docs/user-guide/platform-functionality/status.html +33 -10
  421. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +33 -10
  422. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +33 -10
  423. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +33 -10
  424. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +33 -10
  425. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +33 -10
  426. nautobot/tenancy/tables.py +2 -0
  427. nautobot/virtualization/tests/test_views.py +1 -1
  428. nautobot/wireless/forms.py +0 -1
  429. nautobot/wireless/models.py +1 -1
  430. nautobot/wireless/navigation.py +1 -1
  431. nautobot/wireless/tables.py +18 -3
  432. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/METADATA +4 -4
  433. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/RECORD +439 -419
  434. /nautobot/dcim/templates/dcim/{platform_edit.html → platform_create.html} +0 -0
  435. /nautobot/extras/test_jobs/{pass.py → pass_job.py} +0 -0
  436. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/LICENSE.txt +0 -0
  437. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/NOTICE +0 -0
  438. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/WHEEL +0 -0
  439. {nautobot-2.4.10.dist-info → nautobot-2.4.12.dist-info}/entry_points.txt +0 -0
@@ -6,117 +6,4 @@
6
6
  <li><a href="{% url 'circuits:circuit_list' %}?provider={{ object.circuit.provider.pk }}">{{ object.circuit.provider }}</a></li>
7
7
  <li>{{ object.circuit|hyperlinked_object }}</li>
8
8
  <li>{{ object|hyperlinked_object }}</li>
9
- {% endblock breadcrumbs %}
10
-
11
- {% block content_left_page %}
12
- <div class="panel panel-default">
13
- <table class="table table-hover panel-body attr-table">
14
- <tr>
15
- {% if object.location %}
16
- <tr>
17
- <td>Location</td>
18
- <td>{% include 'dcim/inc/location_hierarchy.html' with location=object.location %}</td>
19
- </tr>
20
- {% endif %}
21
- {% if object.provider_network %}
22
- <td>Provider Network</td>
23
- <td>
24
- {{ object.provider_network|hyperlinked_object }}
25
- </td>
26
- {% endif %}
27
- {% if object.cloud_network %}
28
- <td>Cloud Network</td>
29
- <td>
30
- {{ object.cloud_network|hyperlinked_object }}
31
- </td>
32
- {% endif %}
33
- </tr>
34
- {% if object.location %}
35
- <tr>
36
- <td>Cable</td>
37
- <td>
38
- {% if object.cable %}
39
- {% if perms.dcim.delete_cable %}
40
- <div class="pull-right">
41
- <a href="{% url 'dcim:cable_delete' pk=object.cable.pk %}?return_url={{ object.circuit.get_absolute_url }}" title="Remove cable" class="btn btn-danger btn-xs">
42
- <i class="mdi mdi-ethernet-cable-off" aria-hidden="true"></i> Disconnect
43
- </a>
44
- </div>
45
- {% endif %}
46
- {{ object.cable|hyperlinked_object }}
47
- <a href="{% url 'circuits:circuittermination_trace' pk=object.pk %}" class="btn btn-primary btn-xs" title="Trace">
48
- <i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i>
49
- </a>
50
- {% with peer=object.get_cable_peer %}
51
- to
52
- {% if peer.device %}
53
- {{ peer.device|hyperlinked_object }}
54
- {% elif peer.circuit %}
55
- {{ peer.circuit|hyperlinked_object }}
56
- {% endif %}
57
- ({{ peer }})
58
- {% endwith %}
59
- {% else %}
60
- {% if perms.dcim.add_cable %}
61
- <div class="pull-right">
62
- <span class="dropdown">
63
- <button type="button" class="btn btn-success btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
64
- <span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect
65
- </button>
66
- <ul class="dropdown-menu dropdown-menu-right">
67
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='interface' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Interface</a></li>
68
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='front-port' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Front Port</a></li>
69
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='rear-port' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Rear Port</a></li>
70
- <li><a href="{% url 'circuits:circuittermination_connect' termination_a_id=object.pk termination_b_type='circuit-termination' %}?termination_b_location={{ object.location.pk }}&return_url={{ object.get_absolute_url }}">Circuit Termination</a></li>
71
- </ul>
72
- </span>
73
- </div>
74
- {% endif %}
75
- <span class="text-muted">Not defined</span>
76
- {% endif %}
77
- </td>
78
- </tr>
79
- {% endif %}
80
- <tr>
81
- <td>Speed</td>
82
- <td>
83
- {% if object.port_speed and object.upstream_speed %}
84
- <i class="mdi mdi-arrow-down-bold" title="Downstream"></i> {{ object.port_speed|humanize_speed }} &nbsp;
85
- <i class="mdi mdi-arrow-up-bold" title="Upstream"></i> {{ object.upstream_speed|humanize_speed }}
86
- {% elif object.port_speed %}
87
- {{ object.port_speed|humanize_speed }}
88
- {% else %}
89
- <span class="text-muted">&mdash;</span>
90
- {% endif %}
91
- </td>
92
- </tr>
93
- <tr>
94
- <td>IP Addressing</td>
95
- <td>
96
- {% if object.connected_endpoint %}
97
- {% for ip in object.ip_addresses %}
98
- {% if not forloop.first %}<br />{% endif %}
99
- {{ ip|hyperlinked_object }} ({{ ip.vrf|default:"Global" }})
100
- {% empty %}
101
- <span class="text-muted">None</span>
102
- {% endfor %}
103
- {% else %}
104
- <span class="text-muted">&mdash;</span>
105
- {% endif %}
106
- </td>
107
- </tr>
108
- <tr>
109
- <td>Cross-Connect</td>
110
- <td>{{ object.xconnect_id|placeholder }}</td>
111
- </tr>
112
- <tr>
113
- <td>Patch Panel/Port</td>
114
- <td>{{ object.pp_info|placeholder }}</td>
115
- </tr>
116
- <tr>
117
- <td>Description</td>
118
- <td>{{ object.description|placeholder }}</td>
119
- </tr>
120
- </table>
121
- </div>
122
- {% endblock content_left_page %}
9
+ {% endblock breadcrumbs %}
@@ -23,4 +23,4 @@
23
23
  <span class="mdi mdi-information-outline" aria-hidden="true"></span> Detail
24
24
  </a>
25
25
  {% endif %}
26
- </div>
26
+ </div>
@@ -1,10 +1,10 @@
1
1
  from django.contrib import messages
2
2
  from django.db import transaction
3
3
  from django.shortcuts import get_object_or_404, redirect, render
4
- from django.utils.html import format_html
4
+ from django.utils.html import format_html, format_html_join
5
5
 
6
6
  from nautobot.core.forms import ConfirmationForm
7
- from nautobot.core.templatetags.helpers import bettertitle, humanize_speed, placeholder
7
+ from nautobot.core.templatetags import helpers
8
8
  from nautobot.core.ui.choices import SectionChoices
9
9
  from nautobot.core.ui.object_detail import (
10
10
  ObjectDetailContent,
@@ -13,6 +13,7 @@ from nautobot.core.ui.object_detail import (
13
13
  )
14
14
  from nautobot.core.ui.utils import render_component_template
15
15
  from nautobot.core.views import generic
16
+ from nautobot.core.views.utils import get_obj_from_context
16
17
  from nautobot.core.views.viewsets import NautobotUIViewSet
17
18
 
18
19
  from . import filters, forms, tables
@@ -50,6 +51,38 @@ class CircuitTypeUIViewSet(NautobotUIViewSet):
50
51
  )
51
52
 
52
53
 
54
+ class CircuitTerminationObjectFieldsPanel(ObjectFieldsPanel):
55
+ def get_extra_context(self, context):
56
+ return {"termination": context["object"]}
57
+
58
+ def render_key(self, key, value, context):
59
+ if key == "connected_endpoint":
60
+ return "IP Addressing"
61
+ return super().render_key(key, value, context)
62
+
63
+ def render_value(self, key, value, context):
64
+ instance = get_obj_from_context(context, self.context_object_key)
65
+ location = getattr(instance, "location", None)
66
+
67
+ # Cable column is hidden if the location is unset
68
+ if not location and key == "cable":
69
+ return None
70
+
71
+ if location and key == "cable":
72
+ return render_component_template("circuits/inc/circuit_termination_cable_fragment.html", context)
73
+
74
+ if key == "connected_endpoint":
75
+ ip_addresses = getattr(value, "ip_addresses", None)
76
+ if not ip_addresses or not ip_addresses.exists():
77
+ return helpers.HTML_NONE
78
+ return format_html_join(
79
+ ", ",
80
+ "{} ({})",
81
+ ((helpers.hyperlinked_object(ip), getattr(ip, "vrf", None) or "Global") for ip in ip_addresses.all()),
82
+ )
83
+ return super().render_value(key, value, context)
84
+
85
+
53
86
  class CircuitTerminationUIViewSet(NautobotUIViewSet):
54
87
  action_buttons = ("import", "export")
55
88
  bulk_update_form_class = forms.CircuitTerminationBulkEditForm
@@ -60,6 +93,41 @@ class CircuitTerminationUIViewSet(NautobotUIViewSet):
60
93
  serializer_class = serializers.CircuitTerminationSerializer
61
94
  table_class = tables.CircuitTerminationTable
62
95
 
96
+ object_detail_content = ObjectDetailContent(
97
+ panels=(
98
+ CircuitTerminationObjectFieldsPanel(
99
+ section=SectionChoices.LEFT_HALF,
100
+ weight=100,
101
+ fields=[
102
+ "location",
103
+ "provider_network",
104
+ "cloud_network",
105
+ "cable",
106
+ "port_speed",
107
+ "upstream_speed",
108
+ "connected_endpoint",
109
+ "xconnect_id",
110
+ "pp_info",
111
+ "description",
112
+ ],
113
+ hide_if_unset=[
114
+ "location",
115
+ "provider_network",
116
+ "cloud_network",
117
+ "port_speed",
118
+ "upstream_speed",
119
+ ],
120
+ exclude_fields=[
121
+ "circuit",
122
+ ],
123
+ value_transforms={
124
+ "port_speed": [helpers.humanize_speed],
125
+ "upstream_speed": [helpers.humanize_speed],
126
+ },
127
+ ),
128
+ )
129
+ )
130
+
63
131
  def get_object(self):
64
132
  obj = super().get_object()
65
133
  if self.action in ["create", "update"] and "circuit" in self.kwargs:
@@ -127,8 +195,8 @@ class CircuitUIViewSet(NautobotUIViewSet):
127
195
  "description",
128
196
  ),
129
197
  value_transforms={
130
- "port_speed": [humanize_speed, placeholder],
131
- "upstream_speed": [humanize_speed],
198
+ "port_speed": [helpers.humanize_speed, helpers.placeholder],
199
+ "upstream_speed": [helpers.humanize_speed],
132
200
  },
133
201
  hide_if_unset=("location", "provider_network", "cloud_network", "upstream_speed"),
134
202
  ignore_nonexistent_fields=True, # ip_addresses may be undefined
@@ -173,8 +241,10 @@ class CircuitUIViewSet(NautobotUIViewSet):
173
241
  '<span title="{} ({})">{}</span>',
174
242
  relationship.label,
175
243
  relationship.key,
176
- bettertitle(relationship.get_label(side)),
244
+ helpers.bettertitle(relationship.get_label(side)),
177
245
  )
246
+ if key == "ip_addresses":
247
+ return "IP Addressing"
178
248
  return super().render_key(key, value, context)
179
249
 
180
250
  def render_value(self, key, value, context):
@@ -209,7 +279,7 @@ class CircuitUIViewSet(NautobotUIViewSet):
209
279
  weight=100,
210
280
  fields="__all__",
211
281
  exclude_fields=["comments", "circuit_termination_a", "circuit_termination_z"],
212
- value_transforms={"commit_rate": [humanize_speed, placeholder]},
282
+ value_transforms={"commit_rate": [helpers.humanize_speed, helpers.placeholder]},
213
283
  ),
214
284
  CircuitTerminationPanel(
215
285
  label="Termination - A Side",
@@ -8,7 +8,7 @@ from nautobot.core.apps import (
8
8
  menu_items = (
9
9
  NavMenuTab(
10
10
  name="Cloud",
11
- weight=200,
11
+ weight=150,
12
12
  groups=(
13
13
  NavMenuGroup(
14
14
  name="Cloud",
@@ -34,7 +34,6 @@ class CloudAccountTestCase(ViewTestCases.PrimaryObjectViewTestCase):
34
34
  "provider": providers[1].pk,
35
35
  "secrets_group": secrets_groups[1].pk,
36
36
  "description": "New description",
37
- "comments": "New comments",
38
37
  }
39
38
 
40
39
  def test_post_without_secrets_group(self):
@@ -56,6 +55,12 @@ class CloudAccountTestCase(ViewTestCases.PrimaryObjectViewTestCase):
56
55
 
57
56
  class CloudNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
58
57
  model = CloudNetwork
58
+ custom_action_required_permissions = {
59
+ "cloud:cloudnetwork_children": ["cloud.view_cloudnetwork"],
60
+ "cloud:cloudnetwork_prefixes": ["cloud.view_cloudnetwork", "ipam.view_prefix"],
61
+ "cloud:cloudnetwork_circuits": ["cloud.view_cloudnetwork", "circuits.view_circuit"],
62
+ "cloud:cloudnetwork_cloud_services": ["cloud.view_cloudnetwork", "cloud.view_cloudservice"],
63
+ }
59
64
 
60
65
  @classmethod
61
66
  def setUpTestData(cls):
@@ -104,6 +109,10 @@ class CloudNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
104
109
 
105
110
  class CloudResourceTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
106
111
  model = CloudResourceType
112
+ custom_action_required_permissions = {
113
+ "cloud:cloudresourcetype_services": ["cloud.view_cloudresourcetype", "cloud.view_cloudservice"],
114
+ "cloud:cloudresourcetype_networks": ["cloud.view_cloudresourcetype", "cloud.view_cloudnetwork"],
115
+ }
107
116
 
108
117
  @classmethod
109
118
  def setUpTestData(cls):
@@ -133,6 +142,9 @@ class CloudResourceTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
133
142
 
134
143
  class CloudServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
135
144
  model = CloudService
145
+ custom_action_required_permissions = {
146
+ "cloud:cloudservice_cloud_networks": ["cloud.view_cloudservice", "cloud.view_cloudnetwork"],
147
+ }
136
148
 
137
149
  @classmethod
138
150
  def setUpTestData(cls):
nautobot/cloud/views.py CHANGED
@@ -64,7 +64,6 @@ class CloudNetworkUIViewSet(NautobotUIViewSet):
64
64
  table_class = CloudNetworkTable
65
65
  form_class = CloudNetworkForm
66
66
  bulk_update_form_class = CloudNetworkBulkEditForm
67
-
68
67
  object_detail_content = object_detail.ObjectDetailContent(
69
68
  panels=(
70
69
  object_detail.ObjectFieldsPanel(
@@ -153,19 +152,35 @@ class CloudNetworkUIViewSet(NautobotUIViewSet):
153
152
  ),
154
153
  )
155
154
 
156
- @action(detail=True, url_path="children")
155
+ @action(detail=True, url_path="children", custom_view_base_action="view")
157
156
  def children(self, request, *args, **kwargs):
158
157
  return Response({})
159
158
 
160
- @action(detail=True, url_path="prefixes")
159
+ @action(
160
+ detail=True,
161
+ url_path="prefixes",
162
+ custom_view_base_action="view",
163
+ custom_view_additional_permissions=["ipam.view_prefix"],
164
+ )
161
165
  def prefixes(self, request, *args, **kwargs):
162
166
  return Response({})
163
167
 
164
- @action(detail=True, url_path="circuits")
168
+ @action(
169
+ detail=True,
170
+ url_path="circuits",
171
+ custom_view_base_action="view",
172
+ custom_view_additional_permissions=["circuits.view_circuit"],
173
+ )
165
174
  def circuits(self, request, *args, **kwargs):
166
175
  return Response({})
167
176
 
168
- @action(detail=True, url_path="cloud-services", url_name="cloud_services")
177
+ @action(
178
+ detail=True,
179
+ url_path="cloud-services",
180
+ url_name="cloud_services",
181
+ custom_view_base_action="view",
182
+ custom_view_additional_permissions=["cloud.view_cloudservice"],
183
+ )
169
184
  def cloud_services(self, request, *args, **kwargs):
170
185
  return Response({})
171
186
 
@@ -178,7 +193,6 @@ class CloudResourceTypeUIViewSet(NautobotUIViewSet):
178
193
  table_class = CloudResourceTypeTable
179
194
  form_class = CloudResourceTypeForm
180
195
  bulk_update_form_class = CloudResourceTypeBulkEditForm
181
-
182
196
  object_detail_content = object_detail.ObjectDetailContent(
183
197
  panels=(
184
198
  object_detail.ObjectFieldsPanel(
@@ -230,11 +244,21 @@ class CloudResourceTypeUIViewSet(NautobotUIViewSet):
230
244
  ),
231
245
  )
232
246
 
233
- @action(detail=True, url_path="networks")
247
+ @action(
248
+ detail=True,
249
+ url_path="networks",
250
+ custom_view_base_action="view",
251
+ custom_view_additional_permissions=["cloud.view_cloudnetwork"],
252
+ )
234
253
  def networks(self, request, *args, **kwargs):
235
254
  return Response({})
236
255
 
237
- @action(detail=True, url_path="services")
256
+ @action(
257
+ detail=True,
258
+ url_path="services",
259
+ custom_view_base_action="view",
260
+ custom_view_additional_permissions=["cloud.view_cloudservice"],
261
+ )
238
262
  def services(self, request, *args, **kwargs):
239
263
  return Response({})
240
264
 
@@ -284,6 +308,12 @@ class CloudServiceUIViewSet(NautobotUIViewSet):
284
308
  ),
285
309
  )
286
310
 
287
- @action(detail=True, url_path="cloud-networks", url_name="cloud_networks")
311
+ @action(
312
+ detail=True,
313
+ url_path="cloud-networks",
314
+ url_name="cloud_networks",
315
+ custom_view_base_action="view",
316
+ custom_view_additional_permissions=["cloud.view_cloudnetwork"],
317
+ )
288
318
  def cloud_networks(self, request, *args, **kwargs):
289
319
  return Response({})
@@ -3,10 +3,12 @@ import logging
3
3
  import os
4
4
  from pathlib import Path
5
5
  import shutil
6
+ import sys
6
7
 
7
8
  from celery import Celery, shared_task, signals
8
9
  from celery.app.log import TaskFormatter
9
10
  from celery.utils.log import get_logger
11
+ from django.apps import apps
10
12
  from django.conf import settings
11
13
  from django.db.utils import OperationalError, ProgrammingError
12
14
  from django.utils.functional import SimpleLazyObject
@@ -19,6 +21,7 @@ from nautobot.core.celery.control import discard_git_repository, refresh_git_rep
19
21
  from nautobot.core.celery.encoders import NautobotKombuJSONEncoder
20
22
  from nautobot.core.celery.log import NautobotDatabaseHandler
21
23
  from nautobot.core.utils.module_loading import import_modules_privately
24
+ from nautobot.extras.plugins.utils import import_object
22
25
  from nautobot.extras.registry import registry
23
26
 
24
27
  logger = logging.getLogger(__name__)
@@ -47,12 +50,14 @@ app.autodiscover_tasks()
47
50
  def import_jobs(sender=None, **kwargs):
48
51
  """
49
52
  Import system Jobs into Nautobot as well as Jobs from JOBS_ROOT and GIT_ROOT.
53
+ Import app-provided jobs if the app provides dynamic jobs.
50
54
 
51
55
  Note that app-provided jobs are automatically imported at startup time via NautobotAppConfig.ready()
52
56
  """
53
57
  import nautobot.core.jobs # noqa: F401
54
58
 
55
59
  _import_jobs_from_jobs_root()
60
+ _import_dynamic_jobs_from_apps()
56
61
 
57
62
  try:
58
63
  _import_jobs_from_git_repositories()
@@ -120,6 +125,22 @@ def _import_jobs_from_git_repositories():
120
125
  refresh_git_repository(state=None, repository_pk=repo.pk, head=repo.current_head)
121
126
 
122
127
 
128
+ def _import_dynamic_jobs_from_apps():
129
+ for app_name in settings.PLUGINS:
130
+ app_config = apps.get_app_config(app_name)
131
+ if not getattr(app_config, "provides_dynamic_jobs", False):
132
+ continue
133
+
134
+ # Unload job modules from sys.modules if they were previously loaded
135
+ app_jobs = getattr(app_config, "features", {}).get("jobs", [])
136
+ for job in app_jobs:
137
+ if job.__module__ in sys.modules:
138
+ del sys.modules[job.__module__]
139
+
140
+ # Load app jobs
141
+ app_config.features["jobs"] = import_object(f"{app_config.__module__}.{app_config.jobs}")
142
+
143
+
123
144
  def add_nautobot_log_handler(logger_instance, log_format=None):
124
145
  """Add NautobotDatabaseHandler to logger and update logger level filtering to send all log levels to our handler."""
125
146
  if any(isinstance(h, NautobotDatabaseHandler) for h in logger_instance.handlers):
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from zoneinfo import ZoneInfo
2
3
 
3
4
  from django.db import models
4
5
  from rest_framework.utils.encoders import JSONEncoder
@@ -52,5 +53,7 @@ class NautobotKombuJSONEncoder(JSONEncoder):
52
53
  # JobResult.result uses NautobotKombuJSONEncoder as an encoder and expects a JSONSerializable object,
53
54
  # although an exception, such as a RuntimeException, can be supplied as the obj.
54
55
  return f"{obj.__class__.__name__}: {obj}"
56
+ elif isinstance(obj, ZoneInfo):
57
+ return obj.key
55
58
  else:
56
59
  return super().default(obj)
@@ -138,7 +138,10 @@ class BulkEditForm(forms.Form):
138
138
  # Handle M2M Save
139
139
  for key in self.cleaned_data.keys():
140
140
  if key.startswith(("add_", "remove_")):
141
- field_name = key.lstrip("add_")
141
+ if key.startswith("add_"):
142
+ field_name = key.lstrip("add_")
143
+ else:
144
+ field_name = key.lstrip("remove_")
142
145
  if field_name in m2m_field_names:
143
146
  continue
144
147
  with contextlib.suppress(FieldDoesNotExist):
@@ -96,12 +96,11 @@ class BulkEditObjects(Job):
96
96
  model_field = None
97
97
 
98
98
  # Handle nullification
99
- if nullified_fields:
100
- if field_name in form.nullable_fields and field_name in nullified_fields:
101
- if isinstance(model_field, ManyToManyField):
102
- getattr(obj, field_name).set([])
103
- else:
104
- setattr(obj, field_name, None if model_field is not None and model_field.null else "")
99
+ if nullified_fields and field_name in nullified_fields and field_name in form.nullable_fields:
100
+ if isinstance(model_field, ManyToManyField):
101
+ getattr(obj, field_name).set([])
102
+ else:
103
+ setattr(obj, field_name, None if model_field is not None and model_field.null else "")
105
104
 
106
105
  # ManyToManyFields
107
106
  elif isinstance(model_field, ManyToManyField):
@@ -109,7 +108,8 @@ class BulkEditObjects(Job):
109
108
  getattr(obj, field_name).set(form.cleaned_data[field_name])
110
109
  # Normal fields
111
110
  elif form.cleaned_data[field_name] not in (None, "", []):
112
- setattr(obj, field_name, form.cleaned_data[field_name])
111
+ if hasattr(obj, field_name):
112
+ setattr(obj, field_name, form.cleaned_data[field_name])
113
113
 
114
114
  # Update custom fields
115
115
  for field_name in form_custom_fields:
@@ -121,7 +121,7 @@ class BulkEditObjects(Job):
121
121
  obj.full_clean()
122
122
  obj.save()
123
123
  updated_objects_pk.append(obj.pk)
124
- form.post_save(obj)
124
+ form.post_save(obj) # handles M2M add_* and remove_* form fields
125
125
 
126
126
  if hasattr(form, "save_relationships") and callable(form.save_relationships):
127
127
  # Add/remove relationship associations
@@ -65,6 +65,17 @@ class LogsCleanup(Job):
65
65
  related_model = related_object.related_model
66
66
  related_field_name = related_object.field.name
67
67
  cascade_queryset = related_model.objects.filter(**{f"{related_field_name}__id__in": queryset})
68
+ if cascade_queryset.exists():
69
+ self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
70
+
71
+ genericrelation_related_fields = [
72
+ field for field in queryset.model._meta.private_fields if hasattr(field, "bulk_related_objects")
73
+ ]
74
+ for gr_related_field in genericrelation_related_fields:
75
+ related_model = gr_related_field.related_model
76
+ related_field_name = gr_related_field.related_query_name()
77
+ cascade_queryset = related_model.objects.filter(**{f"{related_field_name}__id__in": queryset})
78
+ if cascade_queryset.exists():
68
79
  self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
69
80
 
70
81
  deleted_count = queryset._raw_delete(using="default")
@@ -225,7 +225,8 @@ class Command(BaseCommand):
225
225
  _create_batch(InterfaceTemplateFactory, 30)
226
226
  _create_batch(PowerPortTemplateFactory, 30)
227
227
  _create_batch(PowerOutletTemplateFactory, 30)
228
- _create_batch(ModuleBayTemplateFactory, 90)
228
+ _create_batch(ModuleBayTemplateFactory, 60, description="without module families", has_module_family=False)
229
+ _create_batch(ModuleBayTemplateFactory, 30, description="with module families", has_module_family=True)
229
230
  _create_batch(ManufacturerFactory, 2, description="without Platforms or DeviceTypes") # Last 2 hard-coded
230
231
  _create_batch(DeviceRedundancyGroupFactory, 20)
231
232
  _create_batch(DeviceFactory, 20)
@@ -1,3 +1,14 @@
1
1
  {% load helpers %}
2
2
 
3
- <strong>{{ body_content_table_verbose_name_plural|bettertitle }}</strong> {% if body_content_table_list_url %}<a href="{{ body_content_table_list_url }}" class="badge badge-primary">{% if badge_count_override %} {{ badge_count_override }}{% else %}{{ body_content_table.rows|length }}{% endif %}</a>{% endif %}
3
+ <strong>{{ body_content_table_verbose_name_plural|bettertitle }}</strong>
4
+ {% if body_content_table_list_url %}
5
+ <a href="{{ body_content_table_list_url }}" class="badge badge-primary">{% if badge_count_override %}{{ badge_count_override }}{% else %}{{ body_content_table.rows|length }}{% endif %}</a>
6
+ {% endif %}
7
+
8
+ <div class="pull-right noprint">
9
+ {% if request.user.is_authenticated %}
10
+ {% table_config_button_small body_content_table %}
11
+ {% endif %}
12
+ </div>
13
+
14
+ {% table_config_form body_content_table %}
@@ -14,3 +14,7 @@
14
14
  </div>
15
15
  {% endif %}
16
16
  </div>
17
+
18
+ {% if body_content_table.paginator %}
19
+ {% include 'inc/paginator.html' with paginator=body_content_table.paginator page=body_content_table.page %}
20
+ {% endif %}
@@ -144,7 +144,7 @@
144
144
  <div class="col-md-6">
145
145
  {% block content_left_page %}{% endblock content_left_page %}
146
146
  {% include 'inc/custom_fields/panel.html' with custom_fields=object.get_custom_field_groupings_basic custom_fields_advanced_ui=False computed_fields=object.get_computed_fields_grouping_basic computed_fields_advanced_ui=False %}
147
- {% include 'inc/relationships_panel.html' %}
147
+ {% include 'inc/relationships/panel_override.html' with relationships_fields_override=object.get_relationships_data_basic_fields %}
148
148
  {% include 'extras/inc/tags_panel.html' %}
149
149
  {% plugin_left_page object %}
150
150
  </div>
@@ -363,4 +363,5 @@
363
363
  }
364
364
  });
365
365
  </script>
366
+ <script src="{% versioned_static 'js/tableconfig.js' %}"></script>
366
367
  {% endblock javascript %}
@@ -5,7 +5,7 @@ from django.apps import apps
5
5
  from django.contrib.auth import get_user_model
6
6
  from django.contrib.contenttypes.models import ContentType
7
7
  from django.core.cache import cache
8
- from django.core.exceptions import FieldDoesNotExist
8
+ from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
9
9
  from django.db import connections, DEFAULT_DB_ALIAS
10
10
  from django.db.models import JSONField, ManyToManyField, ManyToManyRel
11
11
  from django.forms.models import model_to_dict
@@ -161,6 +161,24 @@ class NautobotTestCaseMixin:
161
161
  obj_perm.users.add(self.user)
162
162
  obj_perm.object_types.add(ct)
163
163
 
164
+ def remove_permissions(self, *names, **kwargs):
165
+ """
166
+ Remove a set of permissions. Accepts permission names in the form <app>.<action>_<model>.
167
+ Additional keyword arguments will be passed to the ObjectPermission constructor to allow creating more detailed permissions.
168
+
169
+ Examples:
170
+ >>> remove_permissions("ipam.add_vlangroup", "ipam.view_vlangroup")
171
+ >>> remove_permissions("ipam.add_vlangroup", "ipam.view_vlangroup", constraints={"pk": "uuid-1234"})
172
+ """
173
+ for name in names:
174
+ _, action, _ = permissions.resolve_permission(name)
175
+ try:
176
+ obj_perm = users_models.ObjectPermission.objects.get(name=name, actions=[action], **kwargs)
177
+ obj_perm.delete()
178
+ except ObjectDoesNotExist:
179
+ # Permission does not exist, so nothing to remove
180
+ pass
181
+
164
182
  #
165
183
  # Custom assertions
166
184
  #