nautobot 2.2.3__py3-none-any.whl → 2.2.5__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 (327) hide show
  1. nautobot/circuits/forms.py +15 -0
  2. nautobot/circuits/navigation.py +9 -1
  3. nautobot/circuits/views.py +2 -0
  4. nautobot/core/filters.py +11 -0
  5. nautobot/core/settings.py +6 -4
  6. nautobot/core/settings.yaml +54 -19
  7. nautobot/core/templates/admin/base.html +2 -2
  8. nautobot/core/templates/base_django.html +2 -2
  9. nautobot/core/templates/buttons/export.html +47 -47
  10. nautobot/core/templates/inc/javascript.html +3 -0
  11. nautobot/core/templates/inc/media.html +3 -0
  12. nautobot/core/templates/login.html +2 -2
  13. nautobot/core/templates/nautobot_config.py.j2 +2 -0
  14. nautobot/core/testing/filters.py +24 -1
  15. nautobot/core/testing/views.py +13 -1
  16. nautobot/core/tests/test_jobs.py +79 -2
  17. nautobot/core/tests/test_views.py +33 -0
  18. nautobot/core/views/mixins.py +4 -0
  19. nautobot/core/views/utils.py +18 -1
  20. nautobot/dcim/filters/__init__.py +1 -1
  21. nautobot/dcim/forms.py +23 -4
  22. nautobot/dcim/tables/devicetypes.py +15 -4
  23. nautobot/dcim/tests/test_views.py +323 -55
  24. nautobot/dcim/views.py +26 -20
  25. nautobot/extras/api/serializers.py +17 -6
  26. nautobot/extras/api/views.py +2 -2
  27. nautobot/extras/context_managers.py +3 -0
  28. nautobot/extras/filters/__init__.py +15 -1
  29. nautobot/extras/forms/forms.py +33 -0
  30. nautobot/extras/forms/mixins.py +0 -6
  31. nautobot/extras/signals.py +6 -1
  32. nautobot/extras/tests/test_api.py +24 -2
  33. nautobot/extras/tests/test_context_managers.py +51 -1
  34. nautobot/extras/tests/test_filters.py +69 -0
  35. nautobot/extras/tests/test_forms.py +0 -3
  36. nautobot/extras/tests/test_views.py +48 -4
  37. nautobot/extras/utils.py +2 -1
  38. nautobot/extras/views.py +47 -31
  39. nautobot/ipam/forms.py +18 -0
  40. nautobot/ipam/tests/test_views.py +9 -2
  41. nautobot/ipam/views.py +17 -6
  42. nautobot/project-static/docs/404.html +107 -51
  43. nautobot/project-static/docs/apps/index.html +107 -51
  44. nautobot/project-static/docs/apps/nautobot-apps.html +107 -51
  45. nautobot/project-static/docs/assets/_mkdocstrings.css +6 -1
  46. nautobot/project-static/docs/assets/extra.css +7 -0
  47. nautobot/project-static/docs/assets/javascripts/bundle.ebd0bdb7.min.js +29 -0
  48. nautobot/project-static/docs/assets/javascripts/bundle.ebd0bdb7.min.js.map +7 -0
  49. nautobot/project-static/docs/assets/stylesheets/main.6543a935.min.css +1 -0
  50. nautobot/project-static/docs/assets/stylesheets/main.6543a935.min.css.map +1 -0
  51. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +107 -51
  52. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +107 -51
  53. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +107 -51
  54. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +107 -51
  55. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +107 -51
  56. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +107 -51
  57. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +107 -51
  58. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +107 -51
  59. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +107 -51
  60. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +107 -51
  61. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +107 -51
  62. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +107 -51
  63. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +107 -51
  64. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +107 -51
  65. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +107 -51
  66. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +107 -51
  67. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +107 -51
  68. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +107 -51
  69. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -51
  70. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +107 -51
  71. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +107 -51
  72. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +107 -51
  73. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +107 -51
  74. nautobot/project-static/docs/development/apps/api/configuration-view.html +110 -54
  75. nautobot/project-static/docs/development/apps/api/database-backend-config.html +110 -54
  76. nautobot/project-static/docs/development/apps/api/models/django-admin.html +107 -51
  77. nautobot/project-static/docs/development/apps/api/models/global-search.html +110 -54
  78. nautobot/project-static/docs/development/apps/api/models/graphql.html +113 -57
  79. nautobot/project-static/docs/development/apps/api/models/index.html +107 -51
  80. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +113 -57
  81. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +107 -51
  82. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +111 -55
  83. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +107 -51
  84. nautobot/project-static/docs/development/apps/api/platform-features/index.html +107 -51
  85. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +110 -54
  86. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +110 -54
  87. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +110 -54
  88. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +110 -54
  89. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +107 -51
  90. nautobot/project-static/docs/development/apps/api/prometheus.html +110 -54
  91. nautobot/project-static/docs/development/apps/api/setup.html +107 -51
  92. nautobot/project-static/docs/development/apps/api/testing.html +113 -57
  93. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +110 -54
  94. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +110 -54
  95. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +107 -51
  96. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +107 -51
  97. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +113 -57
  98. nautobot/project-static/docs/development/apps/api/views/base-template.html +107 -51
  99. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +110 -54
  100. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +107 -51
  101. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +110 -54
  102. nautobot/project-static/docs/development/apps/api/views/index.html +107 -51
  103. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +113 -57
  104. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +122 -66
  105. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +110 -54
  106. nautobot/project-static/docs/development/apps/api/views/notes.html +110 -54
  107. nautobot/project-static/docs/development/apps/api/views/rest-api.html +107 -51
  108. nautobot/project-static/docs/development/apps/api/views/urls.html +107 -51
  109. nautobot/project-static/docs/development/apps/index.html +128 -72
  110. nautobot/project-static/docs/development/apps/migration/code-updates.html +107 -51
  111. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +107 -51
  112. nautobot/project-static/docs/development/apps/migration/from-v1.html +109 -53
  113. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +107 -51
  114. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +107 -51
  115. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +107 -51
  116. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +107 -51
  117. nautobot/project-static/docs/development/apps/porting-from-netbox.html +110 -54
  118. nautobot/project-static/docs/development/core/application-registry.html +120 -64
  119. nautobot/project-static/docs/development/core/best-practices.html +122 -66
  120. nautobot/project-static/docs/development/core/bootstrap-ui.html +107 -51
  121. nautobot/project-static/docs/development/core/caching.html +107 -51
  122. nautobot/project-static/docs/development/core/controllers.html +107 -51
  123. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +113 -57
  124. nautobot/project-static/docs/development/core/generic-views.html +110 -54
  125. nautobot/project-static/docs/development/core/getting-started.html +137 -81
  126. nautobot/project-static/docs/development/core/homepage.html +110 -54
  127. nautobot/project-static/docs/development/core/index.html +107 -51
  128. nautobot/project-static/docs/development/core/model-checklist.html +107 -51
  129. nautobot/project-static/docs/development/core/model-features.html +107 -51
  130. nautobot/project-static/docs/development/core/natural-keys.html +110 -54
  131. nautobot/project-static/docs/development/core/navigation-menu.html +107 -51
  132. nautobot/project-static/docs/development/core/release-checklist.html +107 -51
  133. nautobot/project-static/docs/development/core/role-internals.html +107 -51
  134. nautobot/project-static/docs/development/core/settings.html +107 -51
  135. nautobot/project-static/docs/development/core/style-guide.html +110 -54
  136. nautobot/project-static/docs/development/core/templates.html +113 -57
  137. nautobot/project-static/docs/development/core/testing.html +126 -70
  138. nautobot/project-static/docs/development/core/user-preferences.html +107 -51
  139. nautobot/project-static/docs/development/index.html +107 -51
  140. nautobot/project-static/docs/development/jobs/index.html +173 -117
  141. nautobot/project-static/docs/development/jobs/migration/from-v1.html +110 -54
  142. nautobot/project-static/docs/docker/index.html +3 -3
  143. nautobot/project-static/docs/index.html +125 -69
  144. nautobot/project-static/docs/installation/selinux-troubleshooting.html +3 -3
  145. nautobot/project-static/docs/release-notes/index.html +107 -51
  146. nautobot/project-static/docs/release-notes/version-1.0.html +108 -52
  147. nautobot/project-static/docs/release-notes/version-1.1.html +107 -51
  148. nautobot/project-static/docs/release-notes/version-1.2.html +109 -53
  149. nautobot/project-static/docs/release-notes/version-1.3.html +108 -52
  150. nautobot/project-static/docs/release-notes/version-1.4.html +109 -53
  151. nautobot/project-static/docs/release-notes/version-1.5.html +118 -62
  152. nautobot/project-static/docs/release-notes/version-1.6.html +721 -285
  153. nautobot/project-static/docs/release-notes/version-2.0.html +113 -57
  154. nautobot/project-static/docs/release-notes/version-2.1.html +107 -51
  155. nautobot/project-static/docs/release-notes/version-2.2.html +503 -120
  156. nautobot/project-static/docs/requirements.txt +4 -4
  157. nautobot/project-static/docs/search/search_index.json +1 -1
  158. nautobot/project-static/docs/sitemap.xml +262 -262
  159. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  160. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +107 -51
  161. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +107 -51
  162. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +109 -53
  163. nautobot/project-static/docs/user-guide/administration/configuration/index.html +108 -52
  164. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +254 -167
  165. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +113 -57
  166. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +107 -51
  167. nautobot/project-static/docs/user-guide/administration/guides/caching.html +113 -57
  168. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +107 -51
  169. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +107 -51
  170. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +107 -51
  171. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +113 -57
  172. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +108 -52
  173. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +107 -51
  174. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +107 -51
  175. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +171 -112
  176. nautobot/project-static/docs/user-guide/administration/installation/docker.html +13 -8626
  177. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +117 -61
  178. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +13 -8614
  179. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +252 -165
  180. nautobot/project-static/docs/user-guide/administration/installation/index.html +165 -192
  181. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +411 -691
  182. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +249 -230
  183. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +13 -8118
  184. nautobot/project-static/docs/user-guide/administration/installation/services.html +351 -241
  185. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +8684 -0
  186. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +8672 -0
  187. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +8176 -0
  188. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +110 -54
  189. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +110 -54
  190. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +155 -99
  191. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +107 -51
  192. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +109 -53
  193. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +107 -51
  194. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +107 -51
  195. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +108 -52
  196. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +107 -51
  197. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +107 -51
  198. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +107 -70
  199. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +115 -59
  200. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +113 -57
  201. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +107 -51
  202. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +107 -51
  203. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +107 -51
  204. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +107 -51
  205. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +110 -54
  206. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +107 -51
  207. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +110 -54
  208. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +110 -54
  209. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +110 -54
  210. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +110 -54
  211. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +107 -51
  212. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +107 -51
  213. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +115 -59
  214. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +110 -54
  215. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +110 -54
  216. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +110 -54
  217. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +110 -54
  218. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +117 -61
  219. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +110 -54
  220. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +110 -54
  221. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +119 -63
  222. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +110 -54
  223. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +110 -54
  224. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +113 -57
  225. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +113 -57
  226. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +113 -57
  227. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +107 -51
  228. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +113 -57
  229. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +107 -51
  230. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +110 -54
  231. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +110 -54
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +107 -51
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +110 -54
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +110 -54
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +107 -51
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +107 -51
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +107 -51
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +110 -54
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +110 -54
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +110 -54
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +110 -54
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +107 -51
  243. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +113 -57
  244. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +110 -54
  245. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +110 -54
  246. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +110 -54
  247. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +125 -69
  248. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +113 -57
  249. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +128 -72
  250. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +110 -54
  251. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +107 -51
  252. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +110 -54
  253. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +116 -60
  254. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +107 -51
  255. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +113 -57
  256. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +107 -51
  257. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +110 -54
  258. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +107 -51
  259. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +107 -51
  260. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +107 -51
  261. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +107 -51
  262. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +113 -57
  263. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +113 -57
  264. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +107 -51
  265. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +113 -57
  266. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +110 -54
  267. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +107 -51
  268. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +107 -51
  269. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +108 -52
  270. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +107 -51
  271. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +107 -51
  272. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +107 -51
  273. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +107 -51
  274. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +108 -52
  275. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +110 -54
  276. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +113 -57
  277. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +107 -51
  278. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +110 -54
  279. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +107 -51
  280. nautobot/project-static/docs/user-guide/index.html +109 -53
  281. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +107 -51
  282. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +113 -57
  283. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +128 -72
  284. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +107 -51
  285. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +125 -69
  286. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +107 -51
  287. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +110 -54
  288. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +125 -69
  289. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +110 -54
  290. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +107 -51
  291. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +107 -51
  292. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +128 -72
  293. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +114 -58
  294. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +113 -57
  295. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +110 -54
  296. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +121 -65
  297. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +109 -53
  298. nautobot/project-static/docs/user-guide/platform-functionality/note.html +110 -54
  299. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +116 -60
  300. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +110 -54
  301. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +131 -75
  302. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +149 -93
  303. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +110 -54
  304. nautobot/project-static/docs/user-guide/platform-functionality/role.html +108 -84
  305. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +116 -60
  306. nautobot/project-static/docs/user-guide/platform-functionality/status.html +119 -63
  307. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +110 -54
  308. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +137 -81
  309. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +110 -54
  310. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +107 -51
  311. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +110 -54
  312. nautobot/project-static/js/forms.js +2 -1
  313. nautobot/tenancy/forms.py +9 -0
  314. nautobot/tenancy/views.py +3 -6
  315. nautobot/virtualization/forms.py +18 -6
  316. nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
  317. nautobot/virtualization/views.py +7 -9
  318. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/METADATA +2 -2
  319. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/RECORD +323 -320
  320. nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js +0 -29
  321. nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js.map +0 -7
  322. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +0 -1
  323. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +0 -1
  324. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/LICENSE.txt +0 -0
  325. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/NOTICE +0 -0
  326. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/WHEEL +0 -0
  327. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/entry_points.txt +0 -0
@@ -14,8 +14,15 @@ import yaml
14
14
  from nautobot.circuits.choices import CircuitTerminationSideChoices
15
15
  from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider
16
16
  from nautobot.core.templatetags.buttons import job_export_url, job_import_url
17
- from nautobot.core.testing import extract_page_body, ModelViewTestCase, post_data, ViewTestCases
18
- from nautobot.core.testing.utils import generate_random_device_asset_tag_of_specified_size
17
+ from nautobot.core.testing import (
18
+ extract_page_body,
19
+ ModelViewTestCase,
20
+ post_data,
21
+ ViewTestCases,
22
+ )
23
+ from nautobot.core.testing.utils import (
24
+ generate_random_device_asset_tag_of_specified_size,
25
+ )
19
26
  from nautobot.dcim.choices import (
20
27
  CableLengthUnitChoices,
21
28
  CableTypeChoices,
@@ -90,7 +97,11 @@ from nautobot.dcim.models import (
90
97
  SoftwareVersion,
91
98
  VirtualChassis,
92
99
  )
93
- from nautobot.dcim.views import ConsoleConnectionsListView, InterfaceConnectionsListView, PowerConnectionsListView
100
+ from nautobot.dcim.views import (
101
+ ConsoleConnectionsListView,
102
+ InterfaceConnectionsListView,
103
+ PowerConnectionsListView,
104
+ )
94
105
  from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoices
95
106
  from nautobot.extras.models import (
96
107
  ConfigContextSchema,
@@ -132,7 +143,11 @@ def create_test_device(name):
132
143
  devicerole.content_types.add(device_ct)
133
144
  devicestatus = Status.objects.get_for_model(Device).first()
134
145
  device = Device.objects.create(
135
- name=name, location=location, device_type=devicetype, role=devicerole, status=devicestatus
146
+ name=name,
147
+ location=location,
148
+ device_type=devicetype,
149
+ role=devicerole,
150
+ status=devicestatus,
136
151
  )
137
152
 
138
153
  return device
@@ -165,7 +180,10 @@ class LocationTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
165
180
  "name": "Intermediate 2",
166
181
  # "parent": lt1.pk, # TODO: Either overload how EditObjectViewTestCase finds an editable object or write a specific test case for this.
167
182
  "description": "Another intermediate type",
168
- "content_types": [ContentType.objects.get_for_model(Rack).pk, ContentType.objects.get_for_model(Device).pk],
183
+ "content_types": [
184
+ ContentType.objects.get_for_model(Rack).pk,
185
+ ContentType.objects.get_for_model(Device).pk,
186
+ ],
169
187
  "nestable": True,
170
188
  }
171
189
 
@@ -192,7 +210,13 @@ class LocationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
192
210
  loc1 = Location.objects.create(name="Root 1", location_type=lt1, status=status)
193
211
  loc2 = Location.objects.create(name="Root 2", location_type=lt1, status=status, tenant=tenant)
194
212
  loc3 = Location.objects.create(name="Intermediate 1", location_type=lt2, parent=loc2, status=status)
195
- loc4 = Location.objects.create(name="Leaf 1", location_type=lt3, parent=loc3, status=status, description="Hi!")
213
+ loc4 = Location.objects.create(
214
+ name="Leaf 1",
215
+ location_type=lt3,
216
+ parent=loc3,
217
+ status=status,
218
+ description="Hi!",
219
+ )
196
220
  for loc in [loc1, loc2, loc3, loc4]:
197
221
  loc.validated_save()
198
222
 
@@ -228,7 +252,9 @@ class LocationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
228
252
  }
229
253
 
230
254
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
231
- def test_create_child_location_under_a_non_globally_unique_named_parent_location(self):
255
+ def test_create_child_location_under_a_non_globally_unique_named_parent_location(
256
+ self,
257
+ ):
232
258
  self.add_permissions("dcim.add_location")
233
259
  status = Status.objects.get_for_model(Location).first()
234
260
  region_type = LocationType.objects.create(name="Region")
@@ -477,7 +503,11 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
477
503
  cls.cable_connected = cable_statuses.get(name="Connected")
478
504
 
479
505
  cls.custom_fields = (
480
- CustomField.objects.create(type=CustomFieldTypeChoices.TYPE_MULTISELECT, label="Rack Colors", default=[]),
506
+ CustomField.objects.create(
507
+ type=CustomFieldTypeChoices.TYPE_MULTISELECT,
508
+ label="Rack Colors",
509
+ default=[],
510
+ ),
481
511
  )
482
512
 
483
513
  CustomFieldChoice.objects.create(custom_field=cls.custom_fields[0], value="red")
@@ -526,7 +556,9 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
526
556
 
527
557
  for rack in racks:
528
558
  RelationshipAssociation(
529
- relationship=cls.relationships[0], source=rack, destination=cls.locations[1]
559
+ relationship=cls.relationships[0],
560
+ source=rack,
561
+ destination=cls.locations[1],
530
562
  ).validated_save()
531
563
 
532
564
  cls.form_data = {
@@ -635,19 +667,31 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
635
667
  poweroutlet1 = PowerOutlet.objects.create(device=devices[0], name="Power Outlet 11", power_port=powerport1)
636
668
 
637
669
  # connect power port to power feed (single-phase)
638
- cable1 = Cable(termination_a=powerfeed1, termination_b=powerport1, status=self.cable_connected)
670
+ cable1 = Cable(
671
+ termination_a=powerfeed1,
672
+ termination_b=powerport1,
673
+ status=self.cable_connected,
674
+ )
639
675
  cable1.save()
640
676
 
641
677
  # Create power port for 2nd device
642
678
  powerport2 = PowerPort.objects.create(device=devices[1], name="Power Port 12", allocated_draw=1200)
643
679
 
644
680
  # Connect power port to power outlet (dev1)
645
- cable2 = Cable(termination_a=powerport2, termination_b=poweroutlet1, status=self.cable_connected)
681
+ cable2 = Cable(
682
+ termination_a=powerport2,
683
+ termination_b=poweroutlet1,
684
+ status=self.cable_connected,
685
+ )
646
686
  cable2.save()
647
687
 
648
688
  # Create another power port for 2nd device and directly connect to the second PowerFeed.
649
689
  powerport3 = PowerPort.objects.create(device=devices[1], name="Power Port 13", allocated_draw=2400)
650
- cable3 = Cable(termination_a=powerfeed2, termination_b=powerport3, status=self.cable_connected)
690
+ cable3 = Cable(
691
+ termination_a=powerfeed2,
692
+ termination_b=powerport3,
693
+ status=self.cable_connected,
694
+ )
651
695
  cable3.save()
652
696
 
653
697
  # Test the view
@@ -791,7 +835,10 @@ class DeviceTypeTestCase(
791
835
  content,
792
836
  )
793
837
  self.assertInHTML('<input type="hidden" name="export_format" value="yaml">', content)
794
- self.assertInHTML('<button type="submit" class="btn btn-link">YAML format</button>', content)
838
+ self.assertInHTML(
839
+ '<button type="submit" class="btn btn-link" form="export_default">YAML format</button>',
840
+ content,
841
+ )
795
842
  self.assertInHTML('<button type="submit" class="btn btn-link">CSV format</button>', content)
796
843
 
797
844
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
@@ -1431,7 +1478,12 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1431
1478
 
1432
1479
  rack_status = Status.objects.get_for_model(Rack).first()
1433
1480
  racks = (
1434
- Rack.objects.create(name="Rack 1", location=locations[0], rack_group=rack_group, status=rack_status),
1481
+ Rack.objects.create(
1482
+ name="Rack 1",
1483
+ location=locations[0],
1484
+ rack_group=rack_group,
1485
+ status=rack_status,
1486
+ ),
1435
1487
  Rack.objects.create(name="Rack 2", location=locations[1], status=rack_status),
1436
1488
  )
1437
1489
 
@@ -1470,7 +1522,11 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1470
1522
  devicetypes[1].software_image_files.set(software_image_files[2:])
1471
1523
 
1472
1524
  cls.custom_fields = (
1473
- CustomField.objects.create(type=CustomFieldTypeChoices.TYPE_INTEGER, label="Crash Counter", default=0),
1525
+ CustomField.objects.create(
1526
+ type=CustomFieldTypeChoices.TYPE_INTEGER,
1527
+ label="Crash Counter",
1528
+ default=0,
1529
+ ),
1474
1530
  )
1475
1531
  cls.custom_fields[0].content_types.set([ContentType.objects.get_for_model(Device)])
1476
1532
 
@@ -1655,7 +1711,11 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1655
1711
  def test_device_interface_assign_ipaddress(self):
1656
1712
  device = Device.objects.first()
1657
1713
  self.add_permissions(
1658
- "ipam.add_ipaddress", "extras.view_status", "ipam.view_namespace", "dcim.view_device", "dcim.view_interface"
1714
+ "ipam.add_ipaddress",
1715
+ "extras.view_status",
1716
+ "ipam.view_namespace",
1717
+ "dcim.view_device",
1718
+ "dcim.view_interface",
1659
1719
  )
1660
1720
  device_list_url = reverse("dcim:device_interfaces", args=(device.pk,))
1661
1721
  namespace = Namespace.objects.first()
@@ -1691,7 +1751,10 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1691
1751
  self.assertHttpStatus(response, 200)
1692
1752
  self.interfaces[0].refresh_from_db()
1693
1753
  self.assertEqual(self.interfaces[0].ip_addresses.all().count(), 0)
1694
- self.assertIn(f"Interface with id &quot;{self.interfaces[0].pk}&quot; not found", response_body)
1754
+ self.assertIn(
1755
+ f"Interface with id &quot;{self.interfaces[0].pk}&quot; not found",
1756
+ response_body,
1757
+ )
1695
1758
 
1696
1759
  with self.subTest("Assert Cannnot assign IPAddress(Exsisting IP) without permission"):
1697
1760
  # Assert Assign Exsisting IPAddress
@@ -1700,7 +1763,10 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1700
1763
  self.assertHttpStatus(response, 200)
1701
1764
  self.interfaces[1].refresh_from_db()
1702
1765
  self.assertEqual(self.interfaces[1].ip_addresses.all().count(), 0)
1703
- self.assertIn(f"Interface with id &quot;{self.interfaces[1].pk}&quot; not found", response_body)
1766
+ self.assertIn(
1767
+ f"Interface with id &quot;{self.interfaces[1].pk}&quot; not found",
1768
+ response_body,
1769
+ )
1704
1770
 
1705
1771
  self.add_permissions("dcim.change_interface", "ipam.view_ipaddress")
1706
1772
 
@@ -1708,7 +1774,8 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1708
1774
  self.assertHttpStatus(self.client.post(**add_new_ip_request), 302)
1709
1775
  self.interfaces[0].refresh_from_db()
1710
1776
  self.assertEqual(
1711
- str(self.interfaces[0].ip_addresses.all().first().address), add_new_ip_form_data["address"]
1777
+ str(self.interfaces[0].ip_addresses.all().first().address),
1778
+ add_new_ip_form_data["address"],
1712
1779
  )
1713
1780
 
1714
1781
  with self.subTest("Assert Assign IPAddress"):
@@ -1818,7 +1885,8 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1818
1885
  Assert that the local context passes schema validation via full_clean()
1819
1886
  """
1820
1887
  schema = ConfigContextSchema.objects.create(
1821
- name="Schema 1", data_schema={"type": "object", "properties": {"foo": {"type": "string"}}}
1888
+ name="Schema 1",
1889
+ data_schema={"type": "object", "properties": {"foo": {"type": "string"}}},
1822
1890
  )
1823
1891
  self.add_permissions("dcim.add_device")
1824
1892
 
@@ -1832,7 +1900,10 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1832
1900
  "data": post_data(form_data),
1833
1901
  }
1834
1902
  self.assertHttpStatus(self.client.post(**request), 302)
1835
- self.assertEqual(self._get_queryset().get(name="Device X").local_config_context_schema.pk, schema.pk)
1903
+ self.assertEqual(
1904
+ self._get_queryset().get(name="Device X").local_config_context_schema.pk,
1905
+ schema.pk,
1906
+ )
1836
1907
 
1837
1908
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1838
1909
  def test_local_config_context_schema_validation_fails(self):
@@ -1842,7 +1913,8 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1842
1913
  Assert that the local context fails schema validation via full_clean()
1843
1914
  """
1844
1915
  schema = ConfigContextSchema.objects.create(
1845
- name="Schema 1", data_schema={"type": "object", "properties": {"foo": {"type": "integer"}}}
1916
+ name="Schema 1",
1917
+ data_schema={"type": "object", "properties": {"foo": {"type": "integer"}}},
1846
1918
  )
1847
1919
  self.add_permissions("dcim.add_device")
1848
1920
 
@@ -2058,10 +2130,16 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
2058
2130
  Interface.objects.create(device=device, name="Interface 2", status=status_active),
2059
2131
  Interface.objects.create(device=device, name="Interface 3", status=status_active),
2060
2132
  Interface.objects.create(
2061
- device=device, name="LAG", status=status_active, type=InterfaceTypeChoices.TYPE_LAG
2133
+ device=device,
2134
+ name="LAG",
2135
+ status=status_active,
2136
+ type=InterfaceTypeChoices.TYPE_LAG,
2062
2137
  ),
2063
2138
  Interface.objects.create(
2064
- device=device, name="BRIDGE", status=status_active, type=InterfaceTypeChoices.TYPE_BRIDGE
2139
+ device=device,
2140
+ name="BRIDGE",
2141
+ status=status_active,
2142
+ type=InterfaceTypeChoices.TYPE_BRIDGE,
2065
2143
  ),
2066
2144
  )
2067
2145
  cls.lag_interface = interfaces[3]
@@ -2073,16 +2151,32 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
2073
2151
  vlan_group = VLANGroup.objects.first()
2074
2152
  vlans = (
2075
2153
  VLAN.objects.create(
2076
- vid=1, name="VLAN1", location=device.location, status=vlan_status, vlan_group=vlan_group
2154
+ vid=1,
2155
+ name="VLAN1",
2156
+ location=device.location,
2157
+ status=vlan_status,
2158
+ vlan_group=vlan_group,
2077
2159
  ),
2078
2160
  VLAN.objects.create(
2079
- vid=101, name="VLAN101", location=device.location, status=vlan_status, vlan_group=vlan_group
2161
+ vid=101,
2162
+ name="VLAN101",
2163
+ location=device.location,
2164
+ status=vlan_status,
2165
+ vlan_group=vlan_group,
2080
2166
  ),
2081
2167
  VLAN.objects.create(
2082
- vid=102, name="VLAN102", location=device.location, status=vlan_status, vlan_group=vlan_group
2168
+ vid=102,
2169
+ name="VLAN102",
2170
+ location=device.location,
2171
+ status=vlan_status,
2172
+ vlan_group=vlan_group,
2083
2173
  ),
2084
2174
  VLAN.objects.create(
2085
- vid=103, name="VLAN103", location=device.location, status=vlan_status, vlan_group=vlan_group
2175
+ vid=103,
2176
+ name="VLAN103",
2177
+ location=device.location,
2178
+ status=vlan_status,
2179
+ vlan_group=vlan_group,
2086
2180
  ),
2087
2181
  )
2088
2182
 
@@ -2569,22 +2663,37 @@ class CableTestCase(
2569
2663
  circuittype = CircuitType.objects.first()
2570
2664
  circuit_status = Status.objects.get_for_model(Circuit).first()
2571
2665
  circuit = Circuit.objects.create(
2572
- cid="Circuit 1", provider=provider, circuit_type=circuittype, status=circuit_status
2666
+ cid="Circuit 1",
2667
+ provider=provider,
2668
+ circuit_type=circuittype,
2669
+ status=circuit_status,
2573
2670
  )
2574
2671
 
2575
2672
  circuit_terminations = [
2576
2673
  CircuitTermination.objects.create(
2577
- circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A, location=location
2674
+ circuit=circuit,
2675
+ term_side=CircuitTerminationSideChoices.SIDE_A,
2676
+ location=location,
2578
2677
  ),
2579
2678
  CircuitTermination.objects.create(
2580
- circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z, location=location
2679
+ circuit=circuit,
2680
+ term_side=CircuitTerminationSideChoices.SIDE_Z,
2681
+ location=location,
2581
2682
  ),
2582
2683
  ]
2583
2684
 
2584
2685
  status = Status.objects.get_for_model(Cable).get(name="Connected")
2585
2686
  cables = [
2586
- Cable.objects.create(termination_a=circuit_terminations[0], termination_b=interfaces[0], status=status),
2587
- Cable.objects.create(termination_a=circuit_terminations[1], termination_b=interfaces[1], status=status),
2687
+ Cable.objects.create(
2688
+ termination_a=circuit_terminations[0],
2689
+ termination_b=interfaces[0],
2690
+ status=status,
2691
+ ),
2692
+ Cable.objects.create(
2693
+ termination_a=circuit_terminations[1],
2694
+ termination_b=interfaces[1],
2695
+ status=status,
2696
+ ),
2588
2697
  ]
2589
2698
 
2590
2699
  request = {
@@ -2604,7 +2713,10 @@ class CableTestCase(
2604
2713
  cable_path_1 = CablePath.objects.filter(
2605
2714
  Q(origin_type=termination_ct, origin_id=circuit_terminations[0].pk)
2606
2715
  | Q(origin_type=interface_ct, origin_id=interfaces[0].pk)
2607
- | Q(destination_type=termination_ct, destination_id=circuit_terminations[0].pk)
2716
+ | Q(
2717
+ destination_type=termination_ct,
2718
+ destination_id=circuit_terminations[0].pk,
2719
+ )
2608
2720
  | Q(destination_type=interface_ct, destination_id=interfaces[0].pk)
2609
2721
  )
2610
2722
  # pylint: enable=unsupported-binary-operation
@@ -2615,7 +2727,10 @@ class CableTestCase(
2615
2727
  cable_path_2 = CablePath.objects.filter(
2616
2728
  Q(origin_type=termination_ct, origin_id=circuit_terminations[1].pk)
2617
2729
  | Q(origin_type=interface_ct, origin_id=interfaces[1].pk)
2618
- | Q(destination_type=termination_ct, destination_id=circuit_terminations[1].pk)
2730
+ | Q(
2731
+ destination_type=termination_ct,
2732
+ destination_id=circuit_terminations[1].pk,
2733
+ )
2619
2734
  | Q(destination_type=interface_ct, destination_id=interfaces[1].pk)
2620
2735
  )
2621
2736
  # pylint: enable=unsupported-binary-operation
@@ -2660,9 +2775,21 @@ class ConsoleConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2660
2775
  )
2661
2776
  status_connected = Status.objects.get(name="Connected")
2662
2777
 
2663
- Cable.objects.create(termination_a=consoleports[0], termination_b=serverports[0], status=status_connected)
2664
- Cable.objects.create(termination_a=consoleports[1], termination_b=serverports[1], status=status_connected)
2665
- Cable.objects.create(termination_a=consoleports[2], termination_b=rearport, status=status_connected)
2778
+ Cable.objects.create(
2779
+ termination_a=consoleports[0],
2780
+ termination_b=serverports[0],
2781
+ status=status_connected,
2782
+ )
2783
+ Cable.objects.create(
2784
+ termination_a=consoleports[1],
2785
+ termination_b=serverports[1],
2786
+ status=status_connected,
2787
+ )
2788
+ Cable.objects.create(
2789
+ termination_a=consoleports[2],
2790
+ termination_b=rearport,
2791
+ status=status_connected,
2792
+ )
2666
2793
 
2667
2794
 
2668
2795
  class PowerConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
@@ -2709,10 +2836,22 @@ class PowerConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2709
2836
 
2710
2837
  status_connected = Status.objects.get(name="Connected")
2711
2838
 
2712
- Cable.objects.create(termination_a=powerports[2], termination_b=powerfeed, status=status_connected)
2839
+ Cable.objects.create(
2840
+ termination_a=powerports[2],
2841
+ termination_b=powerfeed,
2842
+ status=status_connected,
2843
+ )
2713
2844
  # Creating a PowerOutlet with a PowerPort via the ORM does *not* automatically cable the two together. Bug?
2714
- Cable.objects.create(termination_a=powerports[0], termination_b=poweroutlets[0], status=status_connected)
2715
- Cable.objects.create(termination_a=powerports[1], termination_b=poweroutlets[1], status=status_connected)
2845
+ Cable.objects.create(
2846
+ termination_a=powerports[0],
2847
+ termination_b=poweroutlets[0],
2848
+ status=status_connected,
2849
+ )
2850
+ Cable.objects.create(
2851
+ termination_a=powerports[1],
2852
+ termination_b=poweroutlets[1],
2853
+ status=status_connected,
2854
+ )
2716
2855
 
2717
2856
 
2718
2857
  class InterfaceConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
@@ -2745,18 +2884,30 @@ class InterfaceConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2745
2884
  interface_status = Status.objects.get_for_model(Interface).first()
2746
2885
  cls.interfaces = (
2747
2886
  Interface.objects.create(
2748
- device=device_1, name="Interface 1", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2887
+ device=device_1,
2888
+ name="Interface 1",
2889
+ type=InterfaceTypeChoices.TYPE_1GE_SFP,
2890
+ status=interface_status,
2749
2891
  ),
2750
2892
  Interface.objects.create(
2751
- device=device_1, name="Interface 2", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2893
+ device=device_1,
2894
+ name="Interface 2",
2895
+ type=InterfaceTypeChoices.TYPE_1GE_SFP,
2896
+ status=interface_status,
2752
2897
  ),
2753
2898
  Interface.objects.create(
2754
- device=device_1, name="Interface 3", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2899
+ device=device_1,
2900
+ name="Interface 3",
2901
+ type=InterfaceTypeChoices.TYPE_1GE_SFP,
2902
+ status=interface_status,
2755
2903
  ),
2756
2904
  )
2757
2905
 
2758
2906
  cls.device_2_interface = Interface.objects.create(
2759
- device=device_2, name="Interface 1", type=InterfaceTypeChoices.TYPE_1GE_SFP, status=interface_status
2907
+ device=device_2,
2908
+ name="Interface 1",
2909
+ type=InterfaceTypeChoices.TYPE_1GE_SFP,
2910
+ status=interface_status,
2760
2911
  )
2761
2912
  rearport = RearPort.objects.create(device=device_2, type=PortTypeChoices.TYPE_8P8C)
2762
2913
 
@@ -2764,16 +2915,29 @@ class InterfaceConnectionsTestCase(ViewTestCases.ListObjectsViewTestCase):
2764
2915
  circuittype = CircuitType.objects.first()
2765
2916
  circuit_status = Status.objects.get_for_model(Circuit).first()
2766
2917
  circuit = Circuit.objects.create(
2767
- cid="Circuit 1", provider=provider, circuit_type=circuittype, status=circuit_status
2918
+ cid="Circuit 1",
2919
+ provider=provider,
2920
+ circuit_type=circuittype,
2921
+ status=circuit_status,
2768
2922
  )
2769
2923
  circuittermination = CircuitTermination.objects.create(
2770
- circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A, location=location
2924
+ circuit=circuit,
2925
+ term_side=CircuitTerminationSideChoices.SIDE_A,
2926
+ location=location,
2771
2927
  )
2772
2928
 
2773
2929
  connected = Status.objects.get(name="Connected")
2774
2930
 
2775
- Cable.objects.create(termination_a=cls.interfaces[0], termination_b=cls.device_2_interface, status=connected)
2776
- Cable.objects.create(termination_a=cls.interfaces[1], termination_b=circuittermination, status=connected)
2931
+ Cable.objects.create(
2932
+ termination_a=cls.interfaces[0],
2933
+ termination_b=cls.device_2_interface,
2934
+ status=connected,
2935
+ )
2936
+ Cable.objects.create(
2937
+ termination_a=cls.interfaces[1],
2938
+ termination_b=circuittermination,
2939
+ status=connected,
2940
+ )
2777
2941
  Cable.objects.create(termination_a=cls.interfaces[2], termination_b=rearport, status=connected)
2778
2942
 
2779
2943
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
@@ -2951,12 +3115,23 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2951
3115
  status_planned = statuses[0]
2952
3116
 
2953
3117
  powerfeed_1 = PowerFeed.objects.create(
2954
- name="Power Feed 1", power_panel=powerpanels[0], rack=racks[0], status=status_planned
3118
+ name="Power Feed 1",
3119
+ power_panel=powerpanels[0],
3120
+ rack=racks[0],
3121
+ status=status_planned,
2955
3122
  )
2956
3123
  powerfeed_2 = PowerFeed.objects.create(
2957
- name="Power Feed 2", power_panel=powerpanels[0], rack=racks[0], status=status_planned
3124
+ name="Power Feed 2",
3125
+ power_panel=powerpanels[0],
3126
+ rack=racks[0],
3127
+ status=status_planned,
3128
+ )
3129
+ PowerFeed.objects.create(
3130
+ name="Power Feed 3",
3131
+ power_panel=powerpanels[0],
3132
+ rack=racks[0],
3133
+ status=status_planned,
2958
3134
  )
2959
- PowerFeed.objects.create(name="Power Feed 3", power_panel=powerpanels[0], rack=racks[0], status=status_planned)
2960
3135
 
2961
3136
  # Assign power feeds for the tests later
2962
3137
  cls.powerfeeds = (powerfeed_1, powerfeed_2)
@@ -3009,7 +3184,9 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3009
3184
  powerfeed = self.powerfeeds[0]
3010
3185
 
3011
3186
  Cable.objects.create(
3012
- termination_a=powerport, termination_b=powerfeed, status=Status.objects.get(name="Connected")
3187
+ termination_a=powerport,
3188
+ termination_b=powerfeed,
3189
+ status=Status.objects.get(name="Connected"),
3013
3190
  )
3014
3191
 
3015
3192
  url = reverse("dcim:powerfeed", kwargs={"pk": powerfeed.pk})
@@ -3032,7 +3209,11 @@ class PathTraceViewTestCase(ModelViewTestCase):
3032
3209
  location_type = LocationType.objects.get(name="Campus")
3033
3210
  location = Location.objects.create(location_type=location_type, name="Location 1", status=active)
3034
3211
  device = Device.objects.create(
3035
- device_type=devicetype, role=devicerole, name="Device 1", location=location, status=active
3212
+ device_type=devicetype,
3213
+ role=devicerole,
3214
+ name="Device 1",
3215
+ location=location,
3216
+ status=active,
3036
3217
  )
3037
3218
  obj = RearPort.objects.create(device=device, name="Rear Port 1", type=PortTypeChoices.TYPE_8P8C)
3038
3219
  peer_obj = Interface.objects.create(device=device, name="eth0", status=active)
@@ -3163,7 +3344,10 @@ class InterfaceRedundancyGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3163
3344
 
3164
3345
  # Assign unconstrained permission
3165
3346
  self.add_permissions("dcim.add_interfaceredundancygroupassociation")
3166
- return_url = reverse("dcim:interfaceredundancygroup", kwargs={"pk": self.interface_redundancy_groups[0].pk})
3347
+ return_url = reverse(
3348
+ "dcim:interfaceredundancygroup",
3349
+ kwargs={"pk": self.interface_redundancy_groups[0].pk},
3350
+ )
3167
3351
  url = reverse("dcim:interfaceredundancygroupassociation_add")
3168
3352
  url = url + f"?interface_redundancy_group={self.interface_redundancy_groups[0].pk}&return_url={return_url}"
3169
3353
  self.assertHttpStatus(self.client.get(url), 200)
@@ -3214,6 +3398,48 @@ class SoftwareImageFileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3214
3398
  "download_url": "https://example.com/software_image_file_test_case.bin",
3215
3399
  }
3216
3400
 
3401
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
3402
+ def test_correct_handling_for_model_protected_error(self):
3403
+ platform = Platform.objects.first()
3404
+ software_version_status = Status.objects.get_for_model(SoftwareVersion).first()
3405
+ software_image_file_status = Status.objects.get_for_model(SoftwareImageFile).first()
3406
+ software_version = SoftwareVersion.objects.create(
3407
+ platform=platform, version="Test version 1.0.0", status=software_version_status
3408
+ )
3409
+ software_image_file = SoftwareImageFile.objects.create(
3410
+ software_version=software_version,
3411
+ image_file_name="software_image_file_qs_test_1.bin",
3412
+ status=software_image_file_status,
3413
+ )
3414
+ device_type = DeviceType.objects.first()
3415
+ device_role = Role.objects.get_for_model(Device).first()
3416
+ device_status = Status.objects.get_for_model(Device).first()
3417
+ location = Location.objects.filter(location_type__name="Campus").first()
3418
+ Device.objects.create(
3419
+ device_type=device_type,
3420
+ role=device_role,
3421
+ name="Device 1",
3422
+ location=location,
3423
+ status=device_status,
3424
+ software_version=software_version,
3425
+ )
3426
+ device_type_to_software_image_file = DeviceTypeToSoftwareImageFile.objects.create(
3427
+ device_type=device_type, software_image_file=software_image_file
3428
+ )
3429
+
3430
+ self.add_permissions("dcim.delete_softwareimagefile")
3431
+ pk_list = [software_image_file.pk]
3432
+ data = {
3433
+ "pk": pk_list,
3434
+ "confirm": True,
3435
+ "_confirm": True, # Form button
3436
+ }
3437
+ response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
3438
+ self.assertHttpStatus(response, 200)
3439
+ response_body = response.content.decode(response.charset)
3440
+ # Assert protected error message included in the response body
3441
+ self.assertInHTML(f"<span>{device_type_to_software_image_file}</span>", response_body)
3442
+
3217
3443
 
3218
3444
  class SoftwareVersionTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3219
3445
  model = SoftwareVersion
@@ -3252,6 +3478,48 @@ class SoftwareVersionTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3252
3478
  "pre_release": True,
3253
3479
  }
3254
3480
 
3481
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
3482
+ def test_correct_handling_for_model_protected_error(self):
3483
+ platform = Platform.objects.first()
3484
+ software_version_status = Status.objects.get_for_model(SoftwareVersion).first()
3485
+ software_image_file_status = Status.objects.get_for_model(SoftwareImageFile).first()
3486
+ software_version = SoftwareVersion.objects.create(
3487
+ platform=platform, version="Test version 1.0.0", status=software_version_status
3488
+ )
3489
+ software_image_file = SoftwareImageFile.objects.create(
3490
+ software_version=software_version,
3491
+ image_file_name="software_image_file_qs_test_1.bin",
3492
+ status=software_image_file_status,
3493
+ )
3494
+ device_type = DeviceType.objects.first()
3495
+ device_role = Role.objects.get_for_model(Device).first()
3496
+ device_status = Status.objects.get_for_model(Device).first()
3497
+ location = Location.objects.filter(location_type__name="Campus").first()
3498
+ Device.objects.create(
3499
+ device_type=device_type,
3500
+ role=device_role,
3501
+ name="Device 1",
3502
+ location=location,
3503
+ status=device_status,
3504
+ software_version=software_version,
3505
+ )
3506
+ device_type_to_software_image_file = DeviceTypeToSoftwareImageFile.objects.create(
3507
+ device_type=device_type, software_image_file=software_image_file
3508
+ )
3509
+
3510
+ self.add_permissions("dcim.delete_softwareversion")
3511
+ pk_list = [software_version.pk]
3512
+ data = {
3513
+ "pk": pk_list,
3514
+ "confirm": True,
3515
+ "_confirm": True, # Form button
3516
+ }
3517
+ response = self.client.post(self._get_url("bulk_delete"), data, follow=True)
3518
+ self.assertHttpStatus(response, 200)
3519
+ response_body = response.content.decode(response.charset)
3520
+ # Assert protected error message included in the response body
3521
+ self.assertInHTML(f"<span>{device_type_to_software_image_file}</span>", response_body)
3522
+
3255
3523
 
3256
3524
  class ControllerTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3257
3525
  model = Controller