nautobot 2.4.11__py3-none-any.whl → 2.4.13__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 (364) 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/core/jobs/cleanup.py +4 -4
  6. nautobot/core/templates/components/panel/header_extra_content_table.html +12 -1
  7. nautobot/core/templates/components/panel/panel.html +4 -0
  8. nautobot/core/templates/generic/object_retrieve.html +1 -0
  9. nautobot/core/templates/inc/footer.html +4 -4
  10. nautobot/core/tests/test_utils.py +1 -177
  11. nautobot/core/tests/test_views.py +2 -2
  12. nautobot/core/ui/object_detail.py +1 -1
  13. nautobot/core/utils/module_loading.py +58 -63
  14. nautobot/dcim/filters/__init__.py +6 -0
  15. nautobot/dcim/filters/mixins.py +2 -1
  16. nautobot/dcim/migrations/0071_alter_consoleport_options_and_more.py +42 -0
  17. nautobot/dcim/models/device_components.py +10 -2
  18. nautobot/dcim/models/devices.py +28 -0
  19. nautobot/dcim/tables/devices.py +2 -0
  20. nautobot/dcim/templates/dcim/controller/base.html +1 -9
  21. nautobot/dcim/templates/dcim/controller_retrieve.html +2 -83
  22. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +2 -25
  23. nautobot/dcim/templates/dcim/device.html +1 -34
  24. nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -1
  25. nautobot/dcim/templates/dcim/virtualchassis.html +2 -51
  26. nautobot/dcim/templates/dcim/virtualchassis_add.html +2 -22
  27. nautobot/dcim/templates/dcim/virtualchassis_create.html +22 -0
  28. nautobot/dcim/templates/dcim/virtualchassis_edit.html +2 -93
  29. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +51 -0
  30. nautobot/dcim/templates/dcim/virtualchassis_update.html +93 -0
  31. nautobot/dcim/tests/test_models.py +1 -0
  32. nautobot/dcim/urls.py +2 -59
  33. nautobot/dcim/views.py +180 -191
  34. nautobot/extras/datasources/git.py +2 -2
  35. nautobot/extras/jobs_ui.py +208 -0
  36. nautobot/extras/models/tags.py +4 -0
  37. nautobot/extras/tables.py +1 -1
  38. nautobot/extras/templates/extras/configcontextschema_retrieve.html +2 -2
  39. nautobot/extras/templates/extras/inc/configcontext_data.html +1 -1
  40. nautobot/extras/templates/extras/inc/configcontext_format.html +6 -0
  41. nautobot/extras/templates/extras/inc/json_data.html +1 -1
  42. nautobot/extras/templates/extras/inc/json_format.html +2 -2
  43. nautobot/extras/templates/extras/job_detail.html +1 -326
  44. nautobot/extras/templates/extras/tag_retrieve.html +1 -51
  45. nautobot/extras/views.py +83 -29
  46. nautobot/ipam/migrations/0052_alter_ipaddress_index_together_and_more.py +28 -0
  47. nautobot/ipam/models.py +13 -1
  48. nautobot/ipam/tests/test_api.py +1 -3
  49. nautobot/ipam/utils/testing.py +76 -29
  50. nautobot/project-static/docs/404.html +2 -2
  51. nautobot/project-static/docs/apps/index.html +2 -2
  52. nautobot/project-static/docs/apps/nautobot-apps.html +2 -2
  53. nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js → bundle.56ea9cef.min.js} +2 -2
  54. nautobot/project-static/docs/assets/javascripts/{bundle.13a4f30d.min.js.map → bundle.56ea9cef.min.js.map} +2 -2
  55. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +2 -2
  56. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +2 -2
  57. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +2 -2
  58. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +2 -2
  59. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +2 -2
  60. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +2 -2
  61. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +2 -2
  62. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +2 -2
  63. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +2 -2
  64. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +2 -2
  65. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +2 -2
  66. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2 -2
  67. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2 -2
  68. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +2 -2
  69. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2 -2
  70. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2 -2
  71. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +2 -2
  72. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +2 -2
  73. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +2 -2
  74. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +2 -2
  75. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2 -2
  76. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +2 -2
  77. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2 -2
  78. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +4 -2
  79. nautobot/project-static/docs/development/apps/api/configuration-view.html +2 -2
  80. nautobot/project-static/docs/development/apps/api/database-backend-config.html +2 -2
  81. nautobot/project-static/docs/development/apps/api/models/django-admin.html +2 -2
  82. nautobot/project-static/docs/development/apps/api/models/global-search.html +2 -2
  83. nautobot/project-static/docs/development/apps/api/models/graphql.html +2 -2
  84. nautobot/project-static/docs/development/apps/api/models/index.html +2 -2
  85. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +2 -2
  86. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +2 -2
  87. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +2 -2
  88. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +2 -2
  89. nautobot/project-static/docs/development/apps/api/platform-features/index.html +2 -2
  90. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +2 -2
  91. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +2 -2
  92. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +2 -2
  93. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +2 -2
  94. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +2 -2
  95. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +2 -2
  96. nautobot/project-static/docs/development/apps/api/prometheus.html +2 -2
  97. nautobot/project-static/docs/development/apps/api/setup.html +2 -2
  98. nautobot/project-static/docs/development/apps/api/testing.html +2 -2
  99. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +2 -2
  100. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +2 -2
  101. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +2 -2
  102. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +2 -2
  103. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +2 -2
  104. nautobot/project-static/docs/development/apps/api/views/base-template.html +2 -2
  105. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +2 -2
  106. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +2 -2
  107. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +2 -2
  108. nautobot/project-static/docs/development/apps/api/views/index.html +2 -2
  109. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +2 -2
  110. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +2 -2
  111. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +2 -2
  112. nautobot/project-static/docs/development/apps/api/views/notes.html +2 -2
  113. nautobot/project-static/docs/development/apps/api/views/rest-api.html +2 -2
  114. nautobot/project-static/docs/development/apps/api/views/urls.html +2 -2
  115. nautobot/project-static/docs/development/apps/index.html +2 -2
  116. nautobot/project-static/docs/development/apps/migration/code-updates.html +2 -2
  117. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +2 -2
  118. nautobot/project-static/docs/development/apps/migration/from-v1.html +2 -2
  119. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +2 -2
  120. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +2 -2
  121. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +2 -2
  122. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +2 -2
  123. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +2 -2
  124. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +2 -2
  125. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +2 -2
  126. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +2 -2
  127. nautobot/project-static/docs/development/apps/porting-from-netbox.html +2 -2
  128. nautobot/project-static/docs/development/core/application-registry.html +2 -2
  129. nautobot/project-static/docs/development/core/best-practices.html +2 -2
  130. nautobot/project-static/docs/development/core/bootstrap-ui.html +2 -2
  131. nautobot/project-static/docs/development/core/caching.html +2 -2
  132. nautobot/project-static/docs/development/core/controllers.html +2 -2
  133. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +2 -2
  134. nautobot/project-static/docs/development/core/generic-views.html +2 -2
  135. nautobot/project-static/docs/development/core/getting-started.html +2 -2
  136. nautobot/project-static/docs/development/core/homepage.html +2 -2
  137. nautobot/project-static/docs/development/core/index.html +2 -2
  138. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +2 -2
  139. nautobot/project-static/docs/development/core/model-checklist.html +2 -2
  140. nautobot/project-static/docs/development/core/model-features.html +2 -2
  141. nautobot/project-static/docs/development/core/natural-keys.html +2 -2
  142. nautobot/project-static/docs/development/core/navigation-menu.html +2 -2
  143. nautobot/project-static/docs/development/core/release-checklist.html +2 -2
  144. nautobot/project-static/docs/development/core/role-internals.html +2 -2
  145. nautobot/project-static/docs/development/core/settings.html +2 -2
  146. nautobot/project-static/docs/development/core/style-guide.html +2 -2
  147. nautobot/project-static/docs/development/core/templates.html +2 -2
  148. nautobot/project-static/docs/development/core/testing.html +2 -2
  149. nautobot/project-static/docs/development/core/ui-component-framework.html +2 -2
  150. nautobot/project-static/docs/development/core/user-preferences.html +2 -2
  151. nautobot/project-static/docs/development/index.html +2 -2
  152. nautobot/project-static/docs/development/jobs/getting-started.html +2 -6
  153. nautobot/project-static/docs/development/jobs/index.html +2 -2
  154. nautobot/project-static/docs/development/jobs/installation.html +2 -2
  155. nautobot/project-static/docs/development/jobs/job-extensions.html +2 -2
  156. nautobot/project-static/docs/development/jobs/job-logging.html +2 -2
  157. nautobot/project-static/docs/development/jobs/job-patterns.html +2 -2
  158. nautobot/project-static/docs/development/jobs/job-structure.html +2 -2
  159. nautobot/project-static/docs/development/jobs/migration/from-v1.html +2 -2
  160. nautobot/project-static/docs/development/jobs/testing.html +2 -2
  161. nautobot/project-static/docs/index.html +2 -2
  162. nautobot/project-static/docs/objects.inv +0 -0
  163. nautobot/project-static/docs/overview/application_stack.html +2 -2
  164. nautobot/project-static/docs/overview/design_philosophy.html +2 -2
  165. nautobot/project-static/docs/release-notes/index.html +2 -2
  166. nautobot/project-static/docs/release-notes/version-1.0.html +2 -2
  167. nautobot/project-static/docs/release-notes/version-1.1.html +2 -2
  168. nautobot/project-static/docs/release-notes/version-1.2.html +2 -2
  169. nautobot/project-static/docs/release-notes/version-1.3.html +2 -2
  170. nautobot/project-static/docs/release-notes/version-1.4.html +2 -2
  171. nautobot/project-static/docs/release-notes/version-1.5.html +2 -2
  172. nautobot/project-static/docs/release-notes/version-1.6.html +2 -2
  173. nautobot/project-static/docs/release-notes/version-2.0.html +2 -2
  174. nautobot/project-static/docs/release-notes/version-2.1.html +2 -2
  175. nautobot/project-static/docs/release-notes/version-2.2.html +2 -2
  176. nautobot/project-static/docs/release-notes/version-2.3.html +2 -2
  177. nautobot/project-static/docs/release-notes/version-2.4.html +211 -2
  178. nautobot/project-static/docs/requirements.txt +1 -1
  179. nautobot/project-static/docs/search/search_index.json +1 -1
  180. nautobot/project-static/docs/sitemap.xml +299 -299
  181. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  182. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +2 -2
  183. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +2 -2
  184. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +2 -2
  185. nautobot/project-static/docs/user-guide/administration/configuration/index.html +2 -2
  186. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +2 -2
  187. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -2
  188. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +2 -2
  189. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +2 -2
  190. nautobot/project-static/docs/user-guide/administration/guides/docker.html +2 -2
  191. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +2 -2
  192. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +2 -2
  193. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +2 -2
  194. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +2 -2
  195. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +2 -2
  196. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +2 -2
  197. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +2 -2
  198. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +2 -2
  199. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +2 -2
  200. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +2 -2
  201. nautobot/project-static/docs/user-guide/administration/installation/index.html +2 -2
  202. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +2 -2
  203. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +2 -2
  204. nautobot/project-static/docs/user-guide/administration/installation/services.html +2 -2
  205. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +2 -2
  206. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +2 -2
  207. nautobot/project-static/docs/user-guide/administration/security/index.html +2 -2
  208. nautobot/project-static/docs/user-guide/administration/security/notices.html +2 -2
  209. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +2 -2
  210. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +2 -2
  211. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +2 -2
  212. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +2 -2
  213. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +2 -2
  214. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +2 -2
  215. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +2 -2
  216. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +2 -2
  217. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +2 -2
  218. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +2 -2
  219. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -2
  220. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +2 -2
  221. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +2 -2
  222. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +2 -2
  223. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +2 -2
  224. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +2 -2
  225. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +2 -2
  226. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +2 -2
  227. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +2 -2
  228. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +2 -2
  229. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +2 -2
  230. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +2 -2
  231. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +2 -2
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +2 -2
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +2 -2
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +2 -2
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +2 -2
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +2 -2
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +2 -2
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +2 -2
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +2 -2
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +2 -2
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +2 -2
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +2 -2
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +2 -2
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +2 -2
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +2 -2
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +2 -2
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +2 -2
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +2 -2
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +2 -2
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +2 -2
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +2 -2
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +2 -2
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +2 -2
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +2 -2
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +2 -2
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +2 -2
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +2 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +2 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +2 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +2 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +2 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +2 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +2 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +2 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +2 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +2 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +2 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +2 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +2 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +2 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +2 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +2 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +2 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +2 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +2 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +2 -2
  277. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +2 -2
  278. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +2 -2
  279. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +2 -2
  280. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +2 -2
  281. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +2 -2
  282. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +2 -2
  283. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +2 -2
  284. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +2 -2
  285. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +2 -2
  286. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +2 -2
  287. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +2 -2
  288. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +2 -2
  289. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +2 -2
  290. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +2 -2
  291. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +2 -2
  292. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +2 -2
  293. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +2 -2
  294. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +2 -2
  295. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +2 -2
  296. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +2 -2
  297. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +2 -2
  298. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +2 -2
  299. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +2 -2
  300. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +2 -2
  301. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +2 -2
  302. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +2 -2
  303. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +2 -2
  304. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +2 -2
  305. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +2 -2
  306. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +2 -2
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +2 -2
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +2 -2
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +2 -2
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +2 -2
  311. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +3 -3
  312. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +2 -2
  313. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +2 -2
  314. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +2 -2
  315. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +2 -2
  316. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +2 -2
  317. nautobot/project-static/docs/user-guide/index.html +2 -2
  318. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +2 -2
  319. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +2 -2
  320. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +2 -2
  321. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +2 -2
  322. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +2 -2
  323. nautobot/project-static/docs/user-guide/platform-functionality/events.html +2 -2
  324. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +2 -2
  325. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +2 -2
  326. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +2 -2
  327. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +2 -2
  328. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +2 -2
  329. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +2 -2
  330. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +2 -2
  331. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +2 -2
  332. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +2 -2
  333. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +2 -2
  334. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +2 -2
  335. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +2 -2
  336. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +2 -2
  337. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +2 -2
  338. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +2 -2
  339. nautobot/project-static/docs/user-guide/platform-functionality/note.html +2 -2
  340. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +2 -2
  341. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +2 -2
  342. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +2 -2
  343. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +2 -2
  344. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +2 -2
  345. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +2 -2
  346. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +2 -2
  347. nautobot/project-static/docs/user-guide/platform-functionality/role.html +2 -2
  348. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +2 -2
  349. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +2 -2
  350. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +2 -2
  351. nautobot/project-static/docs/user-guide/platform-functionality/status.html +2 -2
  352. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +2 -2
  353. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +2 -2
  354. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +2 -2
  355. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +2 -2
  356. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +2 -2
  357. nautobot/wireless/navigation.py +1 -1
  358. nautobot/wireless/tables.py +13 -5
  359. {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/METADATA +1 -1
  360. {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/RECORD +364 -357
  361. {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/LICENSE.txt +0 -0
  362. {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/NOTICE +0 -0
  363. {nautobot-2.4.11.dist-info → nautobot-2.4.13.dist-info}/WHEEL +0 -0
  364. {nautobot-2.4.11.dist-info → nautobot-2.4.13.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",
@@ -65,18 +65,18 @@ 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
- self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
68
+ if cascade_queryset.exists():
69
+ self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
69
70
 
70
71
  genericrelation_related_fields = [
71
72
  field for field in queryset.model._meta.private_fields if hasattr(field, "bulk_related_objects")
72
73
  ]
73
74
  for gr_related_field in genericrelation_related_fields:
74
75
  related_model = gr_related_field.related_model
75
- if related_model == queryset.model:
76
- continue # avoid infinite recursion # TODO: this won't catch A<-B<-A<-B loops...
77
76
  related_field_name = gr_related_field.related_query_name()
78
77
  cascade_queryset = related_model.objects.filter(**{f"{related_field_name}__id__in": queryset})
79
- self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
78
+ if cascade_queryset.exists():
79
+ self.recursive_delete_with_cascade(cascade_queryset, deletion_summary)
80
80
 
81
81
  deleted_count = queryset._raw_delete(using="default")
82
82
  if deleted_count:
@@ -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 %}
@@ -363,4 +363,5 @@
363
363
  }
364
364
  });
365
365
  </script>
366
+ <script src="{% versioned_static 'js/tableconfig.js' %}"></script>
366
367
  {% endblock javascript %}
@@ -36,16 +36,16 @@
36
36
  <a href="#theme_modal" data-toggle="modal" data-target="#theme_modal" id="btn-theme-modal"><i class="mdi mdi-theme-light-dark text-primary"></i>Theme</a> &middot;
37
37
  <i class="mdi mdi-book-open-page-variant text-primary"></i>
38
38
  {% if settings.BRANDING_URLS.docs %}
39
- <a href="{{ settings.BRANDING_URLS.docs }}">Docs</a>
39
+ <a href="{{ settings.BRANDING_URLS.docs }}" target="_blank">Docs</a>
40
40
  {% else %}
41
- <a href="{% static 'docs/index.html' %}">Docs</a>
41
+ <a href="{% static 'docs/index.html' %}" target="_blank">Docs</a>
42
42
  {% endif %}
43
43
  &middot;
44
44
  <img src="{% static 'img/jinja_logo.svg' %}" style="height:20px"> <a href="{% url 'render_jinja_template' %}">Jinja Renderer</a> &middot;
45
45
  <i class="mdi mdi-cloud-braces text-primary"></i> <a href="{% url 'api_docs' %}">API</a> &middot;
46
46
  <i class="mdi mdi-graphql text-primary"></i> <a href="{% url 'graphql' %}">GraphQL</a> &middot;
47
- <i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}">Code</a> &middot;
48
- <i class="mdi mdi-lifebuoy text-primary"></i> <a href="{{ settings.BRANDING_URLS.help }}">Help</a>
47
+ <i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}" target="_blank">Code</a> &middot;
48
+ <i class="mdi mdi-lifebuoy text-primary"></i> <a href="{{ settings.BRANDING_URLS.help }}" target="_blank">Help</a>
49
49
  </p>
50
50
  {% endif %}
51
51
  </div>
@@ -1,7 +1,3 @@
1
- import os
2
- import os.path
3
- import sys
4
- import tempfile
5
1
  from unittest import mock
6
2
  import uuid
7
3
 
@@ -21,11 +17,7 @@ from nautobot.core.models import fields as core_fields, utils as models_utils, v
21
17
  from nautobot.core.testing import TestCase
22
18
  from nautobot.core.utils import data as data_utils, filtering, lookup, querysets, requests
23
19
  from nautobot.core.utils.migrations import update_object_change_ct_for_replaced_models
24
- from nautobot.core.utils.module_loading import (
25
- check_name_safe_to_import_privately,
26
- clear_module_from_sys_modules,
27
- import_modules_privately,
28
- )
20
+ from nautobot.core.utils.module_loading import check_name_safe_to_import_privately
29
21
  from nautobot.dcim import filters as dcim_filters, forms as dcim_forms, models as dcim_models, tables
30
22
  from nautobot.extras import models as extras_models, utils as extras_utils
31
23
  from nautobot.extras.choices import ObjectChangeActionChoices, RelationshipTypeChoices
@@ -983,174 +975,6 @@ class TestModuleLoadingUtils(TestCase):
983
975
  self.assertFalse(permitted)
984
976
  self.assertIsInstance(reason, str)
985
977
 
986
- def _create_test_files(self, root_directory: str, contents: dict):
987
- """Helper function to create arbitrary text files in a given directory."""
988
- for relative_path, file_contents in contents.items():
989
- os.makedirs(os.path.dirname(os.path.join(root_directory, relative_path)), exist_ok=True)
990
- with open(os.path.join(root_directory, relative_path), "wt") as fd:
991
- fd.write(file_contents)
992
-
993
- def test_import_modules_privately_jobs_root_case(self):
994
- with tempfile.TemporaryDirectory() as tempdir:
995
- try:
996
- contents = {
997
- # Job file treated as a standalone module
998
- "some_jobs.py": 'name = "some_jobs"',
999
- # Job subdirectory treated as a package
1000
- "my_jobs/__init__.py": '''\
1001
- import my_jobs.some_submodule
1002
- from . import relative_submodule
1003
- name = "my_jobs"''',
1004
- "my_jobs/some_submodule/__init__.py": 'name = "my_jobs.some_submodule"',
1005
- "my_jobs/relative_submodule/__init__.py": 'name = "my_jobs.relative_submodule"',
1006
- # Job file that shouldn't be loaded as it conflicts
1007
- "tkinter.py": 'name = "tkinter"',
1008
- # Job submodule that shouldn't be loaded as it conflicts
1009
- "turtle/__init__.py": 'name = "turtle"',
1010
- }
1011
- self._create_test_files(tempdir, contents)
1012
-
1013
- modules = import_modules_privately(tempdir, ignore_import_errors=False)
1014
- self.assertEqual(["my_jobs", "some_jobs"], sorted([module.__name__ for module in modules]))
1015
- # assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
1016
- if "some_jobs" not in sys.modules:
1017
- self.fail("Valid module wasn't loaded from JOBS_ROOT")
1018
- if "my_jobs" not in sys.modules:
1019
- self.fail("Valid package wasn't loaded from JOBS_ROOT")
1020
- with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from JOBS_ROOT"):
1021
- sys.modules["tkinter"]
1022
- with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from JOBS_ROOT"):
1023
- sys.modules["turtle"]
1024
-
1025
- self.assertEqual(sys.modules["some_jobs"].name, "some_jobs")
1026
- self.assertEqual(sys.modules["my_jobs"].name, "my_jobs")
1027
- self.assertEqual(sys.modules["my_jobs"].some_submodule.name, "my_jobs.some_submodule")
1028
- # self.assertEqual(sys.modules["my_jobs"].relative_submodule.name, "my_jobs.relative_submodule")
1029
-
1030
- finally:
1031
- clear_module_from_sys_modules("some_jobs")
1032
- clear_module_from_sys_modules("my_jobs")
1033
-
1034
- self.assertNotIn("some_jobs", sys.modules.keys())
1035
- self.assertNotIn("my_jobs", sys.modules.keys())
1036
- self.assertNotIn("my_jobs.some_submodule", sys.modules.keys())
1037
-
1038
- # Test reloading of modules after code changes
1039
- try:
1040
- contents["some_jobs.py"] = 'name = "some_jobs_new"'
1041
- contents["my_jobs/__init__.py"] = '''\
1042
- import my_jobs.some_submodule
1043
- from . import relative_submodule
1044
- name = "my_jobs_new"'''
1045
- contents["my_jobs/some_submodule/__init__.py"] = 'name = "my_jobs.some_submodule_new"'
1046
- self._create_test_files(tempdir, contents)
1047
-
1048
- modules = import_modules_privately(tempdir, ignore_import_errors=False)
1049
- self.assertEqual(["my_jobs", "some_jobs"], sorted([module.__name__ for module in modules]))
1050
- # assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
1051
- if "some_jobs" not in sys.modules:
1052
- self.fail("Valid module wasn't loaded from JOBS_ROOT")
1053
- if "my_jobs" not in sys.modules:
1054
- self.fail("Valid package wasn't loaded from JOBS_ROOT")
1055
- with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from JOBS_ROOT"):
1056
- sys.modules["tkinter"]
1057
- with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from JOBS_ROOT"):
1058
- sys.modules["turtle"]
1059
-
1060
- self.assertEqual(sys.modules["some_jobs"].name, "some_jobs_new")
1061
- self.assertEqual(sys.modules["my_jobs"].name, "my_jobs_new")
1062
- self.assertEqual(sys.modules["my_jobs"].some_submodule.name, "my_jobs.some_submodule_new")
1063
-
1064
- finally:
1065
- clear_module_from_sys_modules("some_jobs")
1066
- clear_module_from_sys_modules("my_jobs")
1067
-
1068
- self.assertNotIn("some_jobs", sys.modules.keys())
1069
- self.assertNotIn("my_jobs", sys.modules.keys())
1070
- self.assertNotIn("my_jobs.some_submodule", sys.modules.keys())
1071
-
1072
- def test_import_modules_privately_git_repo_jobs_case(self):
1073
- with tempfile.TemporaryDirectory() as tempdir:
1074
- try:
1075
- contents = {
1076
- # Repo that we intend to load
1077
- "my_repo/__init__.py": 'name = "my_repo"',
1078
- "my_repo/jobs/__init__.py": '''\
1079
- import my_repo.jobs.some_jobs
1080
- from . import some_other_jobs
1081
- name = "my_repo.jobs"''',
1082
- "my_repo/jobs/some_jobs.py": 'name = "my_repo.jobs.some_jobs"',
1083
- "my_repo/jobs/some_other_jobs.py": 'name = "my_repo.jobs.some_other_jobs"',
1084
- # A separate repo, not intended to be loaded
1085
- "other_repo/__init__.py": "",
1086
- # File that shouldn't be loaded as it conflicts
1087
- "tkinter.py": "",
1088
- # Package that shouldn't be loaded as it conflicts
1089
- "turtle/__init__.py": "",
1090
- }
1091
- self._create_test_files(tempdir, contents)
1092
-
1093
- modules = import_modules_privately(tempdir, module_path=["my_repo", "jobs"], ignore_import_errors=False)
1094
- self.assertEqual(["my_repo", "my_repo.jobs"], sorted([module.__name__ for module in modules]))
1095
- # assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
1096
- if "my_repo" not in sys.modules:
1097
- self.fail("Valid repo wasn't loaded from GIT_ROOT")
1098
- if "my_repo.jobs" not in sys.modules:
1099
- self.fail("Valid repo subdirectory wasn't loaded from GIT_ROOT")
1100
- with self.assertRaises(KeyError, msg="unexpected repo was loaded from GIT_ROOT"):
1101
- sys.modules["other_repo"]
1102
- with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from GIT_ROOT"):
1103
- sys.modules["tkinter"]
1104
- with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from GIT_ROOT"):
1105
- sys.modules["turtle"]
1106
-
1107
- self.assertEqual(sys.modules["my_repo"].name, "my_repo")
1108
- self.assertEqual(sys.modules["my_repo.jobs"].name, "my_repo.jobs")
1109
- self.assertEqual(sys.modules["my_repo.jobs"].some_jobs.name, "my_repo.jobs.some_jobs")
1110
- self.assertEqual(sys.modules["my_repo.jobs"].some_other_jobs.name, "my_repo.jobs.some_other_jobs")
1111
-
1112
- finally:
1113
- clear_module_from_sys_modules("my_repo")
1114
-
1115
- self.assertNotIn("my_repo", sys.modules.keys())
1116
- self.assertNotIn("my_repo.jobs", sys.modules.keys())
1117
-
1118
- # Test reloading of modules after code changes
1119
- try:
1120
- contents["my_repo/__init__.py"] = 'name = "my_repo_new"'
1121
- contents["my_repo/jobs/__init__.py"] = '''\
1122
- import my_repo.jobs.some_jobs
1123
- from . import some_other_jobs
1124
- name = "my_repo.jobs_new"'''
1125
- contents["my_repo/jobs/some_jobs.py"] = 'name = "my_repo.jobs.some_jobs_new"'
1126
- contents["my_repo/jobs/some_other_jobs.py"] = 'name = "my_repo.jobs.some_other_jobs_new"'
1127
- self._create_test_files(tempdir, contents)
1128
-
1129
- modules = import_modules_privately(tempdir, module_path=["my_repo", "jobs"], ignore_import_errors=False)
1130
- self.assertEqual(["my_repo", "my_repo.jobs"], sorted([module.__name__ for module in modules]))
1131
- # assertIn/assertNotIn are super noisy when dealing with the huge sys.modules dict, so instead:
1132
- if "my_repo" not in sys.modules:
1133
- self.fail("Valid repo wasn't loaded from GIT_ROOT")
1134
- if "my_repo.jobs" not in sys.modules:
1135
- self.fail("Valid repo subdirectory wasn't loaded from GIT_ROOT")
1136
- with self.assertRaises(KeyError, msg="unexpected repo was loaded from GIT_ROOT"):
1137
- sys.modules["other_repo"]
1138
- with self.assertRaises(KeyError, msg="conflicting module name was loaded unsafely from GIT_ROOT"):
1139
- sys.modules["tkinter"]
1140
- with self.assertRaises(KeyError, msg="conflicting package name was loaded unsafely from GIT_ROOT"):
1141
- sys.modules["turtle"]
1142
-
1143
- self.assertEqual(sys.modules["my_repo"].name, "my_repo_new")
1144
- self.assertEqual(sys.modules["my_repo.jobs"].name, "my_repo.jobs_new")
1145
- self.assertEqual(sys.modules["my_repo.jobs"].some_jobs.name, "my_repo.jobs.some_jobs_new")
1146
- self.assertEqual(sys.modules["my_repo.jobs"].some_other_jobs.name, "my_repo.jobs.some_other_jobs_new")
1147
-
1148
- finally:
1149
- clear_module_from_sys_modules("my_repo")
1150
-
1151
- self.assertNotIn("my_repo", sys.modules.keys())
1152
- self.assertNotIn("my_repo.jobs", sys.modules.keys())
1153
-
1154
978
 
1155
979
  class TestQuerySetUtils(TestCase):
1156
980
  def test_maybe_select_related(self):
@@ -764,11 +764,11 @@ class TestObjectDetailView(TestCase):
764
764
  url = reverse("circuits:provider", args=(provider.pk,))
765
765
  response = self.client.get(f"{url}?tab=main")
766
766
  self.assertHttpStatus(response, 200)
767
- response_data = response.content.decode(response.charset)
767
+ response_data = extract_page_body(response.content.decode(response.charset))
768
768
  view_move_url = reverse("circuits:circuit_list") + f"?provider={provider.id}"
769
769
 
770
770
  # Assert Badge Count in table panel header
771
- panel_header = f"""<div class="panel-heading"><strong>Circuits</strong> <a href="{view_move_url}" class="badge badge-primary">10</a></div>"""
771
+ panel_header = f"""<strong>Circuits</strong> <a href="{view_move_url}" class="badge badge-primary">10</a>"""
772
772
  self.assertInHTML(panel_header, response_data)
773
773
 
774
774
  # Assert view X more btn
@@ -867,7 +867,7 @@ class ObjectsTablePanel(Panel):
867
867
  body_content_table_queryset = body_content_table_queryset.order_by(*self.order_by_fields)
868
868
  body_content_table_queryset = body_content_table_queryset.distinct()
869
869
  body_content_table = body_content_table_class(
870
- body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui
870
+ body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui, user=request.user
871
871
  )
872
872
  if self.tab_id and "actions" in body_content_table.columns:
873
873
  # Use the `self.tab_id`, if it exists, to determine the correct return URL for the table