nautobot 2.4.5__py3-none-any.whl → 2.4.7__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 (410) hide show
  1. nautobot/apps/forms.py +2 -0
  2. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  3. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +2 -55
  4. nautobot/circuits/views.py +20 -23
  5. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +2 -40
  6. nautobot/cloud/views.py +12 -0
  7. nautobot/core/api/mixins.py +10 -0
  8. nautobot/core/api/urls.py +2 -2
  9. nautobot/core/api/views.py +39 -1
  10. nautobot/core/celery/encoders.py +2 -2
  11. nautobot/core/forms/__init__.py +2 -0
  12. nautobot/core/forms/fields.py +23 -7
  13. nautobot/core/forms/utils.py +1 -0
  14. nautobot/core/forms/widgets.py +18 -0
  15. nautobot/core/jobs/bulk_actions.py +1 -1
  16. nautobot/core/management/commands/generate_test_data.py +1 -1
  17. nautobot/core/models/name_color_content_types.py +9 -0
  18. nautobot/core/models/validators.py +7 -0
  19. nautobot/core/settings.py +0 -14
  20. nautobot/core/settings.yaml +0 -28
  21. nautobot/core/tables.py +6 -1
  22. nautobot/core/templates/generic/object_retrieve.html +1 -1
  23. nautobot/core/templates/widgets/sluginput.html +5 -1
  24. nautobot/core/templatetags/helpers.py +15 -1
  25. nautobot/core/testing/api.py +18 -0
  26. nautobot/core/testing/integration.py +6 -2
  27. nautobot/core/tests/nautobot_config.py +0 -2
  28. nautobot/core/tests/runner.py +17 -140
  29. nautobot/core/tests/test_api.py +4 -4
  30. nautobot/core/tests/test_authentication.py +83 -4
  31. nautobot/core/tests/test_forms.py +11 -8
  32. nautobot/core/tests/test_graphql.py +9 -0
  33. nautobot/core/tests/test_jobs.py +7 -0
  34. nautobot/core/ui/object_detail.py +47 -3
  35. nautobot/core/utils/lookup.py +2 -2
  36. nautobot/dcim/factory.py +2 -0
  37. nautobot/dcim/filters/__init__.py +5 -0
  38. nautobot/dcim/forms.py +27 -1
  39. nautobot/dcim/migrations/0068_alter_softwareimagefile_download_url.py +19 -0
  40. nautobot/dcim/migrations/0069_softwareimagefile_external_integration.py +25 -0
  41. nautobot/dcim/models/devices.py +9 -2
  42. nautobot/dcim/models/locations.py +9 -0
  43. nautobot/dcim/tables/devices.py +1 -0
  44. nautobot/dcim/templates/dcim/device_list.html +1 -1
  45. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  46. nautobot/dcim/templates/dcim/manufacturer.html +1 -63
  47. nautobot/dcim/templates/dcim/module_list.html +1 -1
  48. nautobot/dcim/templates/dcim/moduletype_retrieve.html +1 -1
  49. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +4 -0
  50. nautobot/dcim/tests/integration/test_module_bay_position.py +125 -0
  51. nautobot/dcim/tests/test_api.py +74 -31
  52. nautobot/dcim/tests/test_filters.py +2 -0
  53. nautobot/dcim/tests/test_models.py +78 -0
  54. nautobot/dcim/tests/test_views.py +7 -1
  55. nautobot/dcim/urls.py +1 -45
  56. nautobot/dcim/views.py +35 -66
  57. nautobot/extras/choices.py +4 -0
  58. nautobot/extras/filters/__init__.py +6 -0
  59. nautobot/extras/forms/forms.py +83 -11
  60. nautobot/extras/models/customfields.py +10 -9
  61. nautobot/extras/plugins/marketplace_manifest.yml +18 -0
  62. nautobot/extras/signals.py +43 -4
  63. nautobot/extras/tables.py +4 -5
  64. nautobot/extras/tasks.py +4 -2
  65. nautobot/extras/templates/extras/contact_retrieve.html +1 -58
  66. nautobot/extras/templates/extras/exporttemplate.html +1 -53
  67. nautobot/extras/templates/extras/inc/panel_changelog.html +1 -1
  68. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  69. nautobot/extras/templates/extras/status.html +1 -37
  70. nautobot/extras/templates/extras/team_retrieve.html +1 -58
  71. nautobot/extras/tests/integration/test_notes.py +1 -1
  72. nautobot/extras/tests/test_api.py +22 -7
  73. nautobot/extras/tests/test_changelog.py +4 -4
  74. nautobot/extras/tests/test_customfields.py +27 -0
  75. nautobot/extras/tests/test_plugins.py +19 -13
  76. nautobot/extras/tests/test_relationships.py +9 -0
  77. nautobot/extras/tests/test_tags.py +2 -2
  78. nautobot/extras/tests/test_views.py +37 -6
  79. nautobot/extras/urls.py +3 -100
  80. nautobot/extras/views.py +111 -133
  81. nautobot/ipam/tables.py +7 -2
  82. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -41
  83. nautobot/ipam/templates/ipam/service.html +2 -46
  84. nautobot/ipam/templates/ipam/service_edit.html +1 -17
  85. nautobot/ipam/templates/ipam/service_retrieve.html +7 -0
  86. nautobot/ipam/tests/migration/__init__.py +0 -0
  87. nautobot/ipam/tests/migration/test_migrations.py +510 -0
  88. nautobot/ipam/tests/test_api.py +66 -36
  89. nautobot/ipam/tests/test_filters.py +0 -10
  90. nautobot/ipam/tests/test_views.py +44 -2
  91. nautobot/ipam/urls.py +2 -47
  92. nautobot/ipam/utils/migrations.py +185 -152
  93. nautobot/ipam/utils/testing.py +177 -0
  94. nautobot/ipam/views.py +95 -157
  95. nautobot/project-static/css/base.css +5 -0
  96. nautobot/project-static/docs/404.html +0 -2
  97. nautobot/project-static/docs/apps/index.html +0 -2
  98. nautobot/project-static/docs/apps/nautobot-apps.html +0 -2
  99. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +0 -2
  100. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +0 -2
  101. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +0 -2
  102. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +0 -2
  103. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +0 -2
  104. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +0 -2
  105. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +0 -2
  106. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +0 -2
  107. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +0 -2
  108. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +0 -2
  109. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +0 -2
  110. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +0 -2
  111. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +62 -2
  112. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +0 -2
  113. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +0 -2
  114. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +47 -2
  115. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +0 -2
  116. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +0 -2
  117. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -2
  118. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +0 -2
  119. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +70 -5
  120. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +0 -2
  121. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +0 -2
  122. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +0 -2
  123. nautobot/project-static/docs/development/apps/api/configuration-view.html +0 -2
  124. nautobot/project-static/docs/development/apps/api/database-backend-config.html +0 -2
  125. nautobot/project-static/docs/development/apps/api/models/django-admin.html +0 -2
  126. nautobot/project-static/docs/development/apps/api/models/global-search.html +0 -2
  127. nautobot/project-static/docs/development/apps/api/models/graphql.html +0 -2
  128. nautobot/project-static/docs/development/apps/api/models/index.html +0 -2
  129. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +0 -2
  130. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +0 -2
  131. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +0 -2
  132. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +0 -2
  133. nautobot/project-static/docs/development/apps/api/platform-features/index.html +0 -2
  134. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +0 -2
  135. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +0 -2
  136. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +0 -2
  137. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +0 -2
  138. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +0 -2
  139. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +0 -2
  140. nautobot/project-static/docs/development/apps/api/prometheus.html +0 -2
  141. nautobot/project-static/docs/development/apps/api/setup.html +0 -2
  142. nautobot/project-static/docs/development/apps/api/testing.html +0 -89
  143. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +0 -2
  144. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +0 -2
  145. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +0 -2
  146. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +0 -2
  147. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +0 -2
  148. nautobot/project-static/docs/development/apps/api/views/base-template.html +0 -2
  149. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +0 -2
  150. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +0 -2
  151. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +0 -2
  152. nautobot/project-static/docs/development/apps/api/views/index.html +0 -2
  153. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +0 -2
  154. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +0 -2
  155. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +0 -2
  156. nautobot/project-static/docs/development/apps/api/views/notes.html +0 -2
  157. nautobot/project-static/docs/development/apps/api/views/rest-api.html +0 -2
  158. nautobot/project-static/docs/development/apps/api/views/urls.html +0 -2
  159. nautobot/project-static/docs/development/apps/index.html +0 -2
  160. nautobot/project-static/docs/development/apps/migration/code-updates.html +0 -2
  161. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +1 -3
  162. nautobot/project-static/docs/development/apps/migration/from-v1.html +0 -2
  163. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +0 -2
  164. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +0 -2
  165. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +0 -2
  166. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +0 -2
  167. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +0 -2
  168. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +0 -2
  169. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +0 -2
  170. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +0 -2
  171. nautobot/project-static/docs/development/apps/porting-from-netbox.html +0 -2
  172. nautobot/project-static/docs/development/core/application-registry.html +0 -2
  173. nautobot/project-static/docs/development/core/best-practices.html +3 -5
  174. nautobot/project-static/docs/development/core/bootstrap-ui.html +0 -2
  175. nautobot/project-static/docs/development/core/caching.html +0 -2
  176. nautobot/project-static/docs/development/core/controllers.html +0 -2
  177. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +0 -2
  178. nautobot/project-static/docs/development/core/generic-views.html +0 -2
  179. nautobot/project-static/docs/development/core/getting-started.html +78 -109
  180. nautobot/project-static/docs/development/core/homepage.html +0 -2
  181. nautobot/project-static/docs/development/core/index.html +0 -2
  182. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +0 -2
  183. nautobot/project-static/docs/development/core/model-checklist.html +0 -2
  184. nautobot/project-static/docs/development/core/model-features.html +0 -2
  185. nautobot/project-static/docs/development/core/natural-keys.html +0 -2
  186. nautobot/project-static/docs/development/core/navigation-menu.html +0 -2
  187. nautobot/project-static/docs/development/core/release-checklist.html +1 -3
  188. nautobot/project-static/docs/development/core/role-internals.html +0 -2
  189. nautobot/project-static/docs/development/core/settings.html +0 -2
  190. nautobot/project-static/docs/development/core/style-guide.html +1 -3
  191. nautobot/project-static/docs/development/core/templates.html +0 -2
  192. nautobot/project-static/docs/development/core/testing.html +24 -200
  193. nautobot/project-static/docs/development/core/ui-component-framework.html +0 -2
  194. nautobot/project-static/docs/development/core/user-preferences.html +0 -2
  195. nautobot/project-static/docs/development/index.html +0 -2
  196. nautobot/project-static/docs/development/jobs/index.html +0 -2
  197. nautobot/project-static/docs/development/jobs/migration/from-v1.html +0 -2
  198. nautobot/project-static/docs/index.html +0 -2
  199. nautobot/project-static/docs/media/user-guide/administration/getting-started/nautobot-cloud.png +0 -0
  200. nautobot/project-static/docs/objects.inv +0 -0
  201. nautobot/project-static/docs/overview/application_stack.html +1 -3
  202. nautobot/project-static/docs/overview/design_philosophy.html +0 -2
  203. nautobot/project-static/docs/release-notes/index.html +0 -2
  204. nautobot/project-static/docs/release-notes/version-1.0.html +0 -2
  205. nautobot/project-static/docs/release-notes/version-1.1.html +0 -2
  206. nautobot/project-static/docs/release-notes/version-1.2.html +0 -2
  207. nautobot/project-static/docs/release-notes/version-1.3.html +0 -2
  208. nautobot/project-static/docs/release-notes/version-1.4.html +0 -2
  209. nautobot/project-static/docs/release-notes/version-1.5.html +0 -2
  210. nautobot/project-static/docs/release-notes/version-1.6.html +0 -2
  211. nautobot/project-static/docs/release-notes/version-2.0.html +0 -2
  212. nautobot/project-static/docs/release-notes/version-2.1.html +0 -2
  213. nautobot/project-static/docs/release-notes/version-2.2.html +0 -2
  214. nautobot/project-static/docs/release-notes/version-2.3.html +0 -2
  215. nautobot/project-static/docs/release-notes/version-2.4.html +364 -3
  216. nautobot/project-static/docs/search/search_index.json +1 -1
  217. nautobot/project-static/docs/sitemap.xml +290 -290
  218. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  219. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +0 -2
  220. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +0 -2
  221. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +0 -2
  222. nautobot/project-static/docs/user-guide/administration/configuration/index.html +0 -2
  223. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +0 -2
  224. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -50
  225. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +0 -2
  226. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +0 -2
  227. nautobot/project-static/docs/user-guide/administration/guides/docker.html +0 -2
  228. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +0 -2
  229. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +71 -2
  230. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +0 -2
  231. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +0 -2
  232. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +0 -2
  233. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +0 -2
  234. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +0 -2
  235. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +0 -2
  236. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +0 -2
  237. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -3
  238. nautobot/project-static/docs/user-guide/administration/installation/index.html +257 -18
  239. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +0 -2
  240. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +0 -2
  241. nautobot/project-static/docs/user-guide/administration/installation/services.html +0 -2
  242. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +0 -2
  243. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +0 -2
  244. nautobot/project-static/docs/user-guide/administration/security/index.html +0 -2
  245. nautobot/project-static/docs/user-guide/administration/security/notices.html +0 -2
  246. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +1 -3
  247. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +0 -2
  248. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +0 -2
  249. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +0 -2
  250. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +0 -2
  251. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +0 -2
  252. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +0 -2
  253. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +0 -2
  254. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +0 -2
  255. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +0 -2
  256. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -4
  257. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +0 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +0 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +0 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +0 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +0 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +0 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +0 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +0 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +0 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +0 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +0 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +0 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +0 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +0 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +0 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +0 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +0 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +0 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +0 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +0 -2
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +0 -2
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +0 -2
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +0 -2
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +0 -2
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +0 -2
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +0 -2
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +0 -2
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +0 -2
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +0 -2
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +0 -2
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +0 -2
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +0 -2
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +0 -2
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +0 -2
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +0 -2
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +0 -2
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +0 -2
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +0 -2
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +0 -2
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +0 -2
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +0 -2
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +0 -2
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +0 -2
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +0 -2
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +0 -2
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +0 -2
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +0 -2
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +0 -2
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +0 -2
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +0 -2
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -2
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +0 -2
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +0 -2
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +0 -2
  311. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +0 -2
  312. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +0 -2
  313. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +0 -2
  314. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +0 -2
  315. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +0 -2
  316. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +0 -2
  317. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +0 -2
  318. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +0 -2
  319. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +0 -2
  320. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +0 -2
  321. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +0 -2
  322. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +0 -2
  323. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +0 -2
  324. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +0 -2
  325. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +0 -2
  326. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +0 -2
  327. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +0 -2
  328. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +0 -2
  329. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +0 -2
  330. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +0 -2
  331. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +0 -2
  332. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +0 -2
  333. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +0 -2
  334. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +0 -2
  335. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +0 -2
  336. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -13
  337. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +0 -2
  338. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +8 -10
  339. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +1 -2
  340. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +0 -2
  341. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +40 -27
  342. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -6
  343. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +1 -3
  344. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +77 -7
  345. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +1 -3
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +0 -3
  347. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -3
  348. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +0 -2
  349. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +0 -2
  350. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +0 -2
  351. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +0 -2
  352. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -2
  353. nautobot/project-static/docs/user-guide/index.html +89 -4
  354. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +0 -2
  355. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +0 -2
  356. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +0 -2
  357. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +0 -2
  358. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +0 -2
  359. nautobot/project-static/docs/user-guide/platform-functionality/events.html +0 -2
  360. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +0 -2
  361. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +0 -2
  362. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +0 -2
  363. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +0 -2
  364. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +0 -2
  365. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +0 -2
  366. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +0 -2
  367. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +0 -2
  368. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +0 -2
  369. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +0 -2
  370. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +0 -2
  371. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -2
  372. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +0 -2
  373. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +0 -2
  374. nautobot/project-static/docs/user-guide/platform-functionality/note.html +0 -2
  375. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +0 -2
  376. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +0 -2
  377. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +0 -2
  378. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +0 -2
  379. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +0 -2
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +0 -2
  381. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +0 -2
  382. nautobot/project-static/docs/user-guide/platform-functionality/role.html +0 -2
  383. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +0 -2
  384. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +0 -2
  385. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +0 -2
  386. nautobot/project-static/docs/user-guide/platform-functionality/status.html +0 -2
  387. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +0 -2
  388. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +0 -2
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +0 -2
  390. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +0 -2
  391. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +207 -124
  392. nautobot/project-static/js/forms.js +88 -37
  393. nautobot/project-static/js/homepage_layout.js +12 -3
  394. nautobot/virtualization/forms.py +20 -0
  395. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -39
  396. nautobot/virtualization/templates/virtualization/clustertype.html +1 -0
  397. nautobot/virtualization/tests/test_api.py +14 -3
  398. nautobot/virtualization/tests/test_views.py +10 -2
  399. nautobot/virtualization/urls.py +10 -93
  400. nautobot/virtualization/views.py +33 -72
  401. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/METADATA +6 -5
  402. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/RECORD +407 -402
  403. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/WHEEL +1 -1
  404. nautobot/core/tests/performance_baselines.yml +0 -8900
  405. nautobot/dcim/templates/dcim/modulebay_create.html +0 -39
  406. nautobot/ipam/tests/test_migrations.py +0 -462
  407. /nautobot/ipam/templates/ipam/{namespace_ipaddresses.html → namespace_ip_addresses.html} +0 -0
  408. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/LICENSE.txt +0 -0
  409. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/NOTICE +0 -0
  410. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/entry_points.txt +0 -0
@@ -68,7 +68,7 @@ from nautobot.dcim.models import (
68
68
  VirtualChassis,
69
69
  VirtualDeviceContext,
70
70
  )
71
- from nautobot.extras.models import ConfigContextSchema, Role, SecretsGroup, Status
71
+ from nautobot.extras.models import ConfigContextSchema, ExternalIntegration, Role, SecretsGroup, Status
72
72
  from nautobot.ipam.models import IPAddress, Namespace, Prefix, VLAN, VLANGroup
73
73
  from nautobot.tenancy.models import Tenant
74
74
  from nautobot.virtualization.models import Cluster, ClusterType
@@ -187,7 +187,12 @@ class Mixins:
187
187
  def test_module_device_validation(self):
188
188
  """Assert that a modular component can have a module or a device but not both."""
189
189
 
190
- self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
190
+ self.add_permissions(
191
+ f"{self.model._meta.app_label}.add_{self.model._meta.model_name}",
192
+ "dcim.view_device",
193
+ "dcim.view_module",
194
+ "extras.view_status",
195
+ )
191
196
  data = {
192
197
  self.module_field: self.module.pk,
193
198
  self.device_field: self.device.pk,
@@ -220,7 +225,12 @@ class Mixins:
220
225
  def test_module_device_name_unique_validation(self):
221
226
  """Assert uniqueness constraint is enforced for (device,name) and (module,name) fields."""
222
227
 
223
- self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
228
+ self.add_permissions(
229
+ f"{self.model._meta.app_label}.add_{self.model._meta.model_name}",
230
+ "dcim.view_device",
231
+ "dcim.view_module",
232
+ "extras.view_status",
233
+ )
224
234
  modules = Module.objects.all()[:2]
225
235
  data = {
226
236
  self.module_field: modules[0].pk,
@@ -266,7 +276,11 @@ class Mixins:
266
276
  def test_module_type_device_type_validation(self):
267
277
  """Assert that a modular component template can have a module_type or a device_type but not both."""
268
278
 
269
- self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
279
+ self.add_permissions(
280
+ f"{self.model._meta.app_label}.add_{self.model._meta.model_name}",
281
+ "dcim.view_devicetype",
282
+ "dcim.view_moduletype",
283
+ )
270
284
  data = {
271
285
  "module_type": self.module_type.pk,
272
286
  "device_type": self.device_type.pk,
@@ -299,7 +313,11 @@ class Mixins:
299
313
  def test_module_type_device_type_name_unique_validation(self):
300
314
  """Assert uniqueness constraint is enforced for (device_type,name) and (module_type,name) fields."""
301
315
 
302
- self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
316
+ self.add_permissions(
317
+ f"{self.model._meta.app_label}.add_{self.model._meta.model_name}",
318
+ "dcim.view_devicetype",
319
+ "dcim.view_moduletype",
320
+ )
303
321
  module_types = ModuleType.objects.all()[:2]
304
322
  data = {
305
323
  "module_type": module_types[0].pk,
@@ -442,7 +460,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelA
442
460
  Test allow_null to time_zone field on locaton.
443
461
  """
444
462
 
445
- self.add_permissions("dcim.add_location")
463
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_status")
446
464
  url = reverse("dcim-api:location-list")
447
465
  location = {
448
466
  "name": "foo",
@@ -461,7 +479,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelA
461
479
  Test disallowed blank time_zone field on location.
462
480
  """
463
481
 
464
- self.add_permissions("dcim.add_location")
482
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_status")
465
483
  url = reverse("dcim-api:location-list")
466
484
  location = {
467
485
  "name": "foo",
@@ -480,7 +498,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelA
480
498
  Test valid time_zone field on location.
481
499
  """
482
500
 
483
- self.add_permissions("dcim.add_location")
501
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_status")
484
502
  url = reverse("dcim-api:location-list")
485
503
  time_zone = "UTC"
486
504
  location = {
@@ -500,7 +518,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModelA
500
518
  Test invalid time_zone field on location.
501
519
  """
502
520
 
503
- self.add_permissions("dcim.add_location")
521
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_status")
504
522
  url = reverse("dcim-api:location-list")
505
523
  time_zone = "IDONOTEXIST"
506
524
  location = {
@@ -593,7 +611,7 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModel
593
611
 
594
612
  def test_child_group_location_valid(self):
595
613
  """A child group with a location may fall within the parent group's location."""
596
- self.add_permissions("dcim.add_rackgroup")
614
+ self.add_permissions("dcim.add_rackgroup", "dcim.view_rackgroup", "dcim.view_location")
597
615
  url = reverse("dcim-api:rackgroup-list")
598
616
 
599
617
  parent_group = RackGroup.objects.filter(location=self.locations[0]).first()
@@ -615,7 +633,7 @@ class RackGroupTest(APIViewTestCases.APIViewTestCase, APIViewTestCases.TreeModel
615
633
 
616
634
  def test_child_group_location_invalid(self):
617
635
  """A child group with a location must not fall outside its parent group's location."""
618
- self.add_permissions("dcim.add_rackgroup")
636
+ self.add_permissions("dcim.add_rackgroup", "dcim.view_location", "dcim.view_rackgroup")
619
637
  url = reverse("dcim-api:rackgroup-list")
620
638
 
621
639
  parent_group = RackGroup.objects.filter(location=self.locations[0]).first()
@@ -1225,7 +1243,9 @@ class FrontPortTemplateTest(Mixins.BasePortTemplateTestMixin):
1225
1243
  def test_module_type_device_type_validation(self):
1226
1244
  """Assert that a modular component template can have a module_type or a device_type but not both."""
1227
1245
 
1228
- self.add_permissions("dcim.add_frontporttemplate")
1246
+ self.add_permissions(
1247
+ "dcim.add_frontporttemplate", "dcim.view_rearporttemplate", "dcim.view_devicetype", "dcim.view_moduletype"
1248
+ )
1229
1249
  data = {
1230
1250
  "module_type": self.module_type.pk,
1231
1251
  "device_type": self.device_type.pk,
@@ -1253,7 +1273,9 @@ class FrontPortTemplateTest(Mixins.BasePortTemplateTestMixin):
1253
1273
  def test_module_type_device_type_name_unique_validation(self):
1254
1274
  """Assert uniqueness constraint is enforced for (device_type,name) and (module_type,name) fields."""
1255
1275
 
1256
- self.add_permissions("dcim.add_frontporttemplate")
1276
+ self.add_permissions(
1277
+ "dcim.add_frontporttemplate", "dcim.view_rearporttemplate", "dcim.view_moduletype", "dcim.view_devicetype"
1278
+ )
1257
1279
  data = {
1258
1280
  "module_type": self.module_type.pk,
1259
1281
  "name": "test modular device_type component parent validation",
@@ -1617,7 +1639,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1617
1639
  schema = ConfigContextSchema.objects.create(
1618
1640
  name="Schema 1", data_schema={"type": "object", "properties": {"A": {"type": "integer"}}}
1619
1641
  )
1620
- self.add_permissions("dcim.change_device")
1642
+ self.add_permissions("dcim.change_device", "extras.view_configcontextschema")
1621
1643
 
1622
1644
  patch_data = {"local_config_context_schema": str(schema.pk)}
1623
1645
 
@@ -1651,7 +1673,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1651
1673
  Validate we can set primary_ip4 on a device using a PATCH.
1652
1674
  """
1653
1675
  # Add object-level permission
1654
- self.add_permissions("dcim.change_device")
1676
+ self.add_permissions("dcim.change_device", "ipam.view_ipaddress")
1655
1677
 
1656
1678
  dev = Device.objects.get(name="Device 3")
1657
1679
  intf_status = Status.objects.get_for_model(Interface).first()
@@ -1677,7 +1699,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1677
1699
  Validate we can set device redundancy group on a device using a PATCH.
1678
1700
  """
1679
1701
  # Add object-level permission
1680
- self.add_permissions("dcim.change_device")
1702
+ self.add_permissions("dcim.change_device", "dcim.view_deviceredundancygroup")
1681
1703
 
1682
1704
  device_redundancy_group = DeviceRedundancyGroup.objects.first()
1683
1705
 
@@ -1751,7 +1773,15 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1751
1773
  # Create test data
1752
1774
  parent_device, device_bay_1, device_bay_2, device_type_child = self._parent_device_test_data()
1753
1775
 
1754
- self.add_permissions("dcim.add_device")
1776
+ self.add_permissions(
1777
+ "dcim.add_device",
1778
+ "dcim.view_device",
1779
+ "dcim.view_devicetype",
1780
+ "extras.view_role",
1781
+ "extras.view_status",
1782
+ "dcim.view_location",
1783
+ "dcim.view_devicebay",
1784
+ )
1755
1785
  url = reverse("dcim-api:device-list")
1756
1786
 
1757
1787
  # Test creating device with parent bay by device bay data
@@ -1808,7 +1838,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1808
1838
  # Create test data
1809
1839
  parent_device, device_bay_1, device_bay_2, device_type_child = self._parent_device_test_data()
1810
1840
 
1811
- self.add_permissions("dcim.change_device")
1841
+ self.add_permissions("dcim.change_device", "dcim.view_devicebay")
1812
1842
 
1813
1843
  child_device = Device.objects.create(
1814
1844
  device_type=device_type_child,
@@ -1852,7 +1882,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
1852
1882
  device_bay_1.installed_device = child_device
1853
1883
  device_bay_1.save()
1854
1884
 
1855
- self.add_permissions("dcim.change_device", "dcim.view_device", "dcim.change_devicebay")
1885
+ self.add_permissions("dcim.change_device", "dcim.view_device", "dcim.change_devicebay", "dcim.view_devicebay")
1856
1886
  child_device_detail_url = self._get_detail_url(child_device)
1857
1887
 
1858
1888
  response = self.client.get(child_device_detail_url, **self.header)
@@ -1950,7 +1980,9 @@ class ModuleTestCase(APIViewTestCases.APIViewTestCase):
1950
1980
  def test_parent_module_bay_location_validation(self):
1951
1981
  """Assert that a module can have a parent_module_bay or a location but not both."""
1952
1982
 
1953
- self.add_permissions("dcim.add_module")
1983
+ self.add_permissions(
1984
+ "dcim.add_module", "dcim.view_moduletype", "dcim.view_location", "dcim.view_modulebay", "extras.view_status"
1985
+ )
1954
1986
  data = {
1955
1987
  "module_type": self.module_type.pk,
1956
1988
  "location": self.location.pk,
@@ -1981,7 +2013,7 @@ class ModuleTestCase(APIViewTestCases.APIViewTestCase):
1981
2013
  )
1982
2014
 
1983
2015
  def test_serial_module_type_unique_validation(self):
1984
- self.add_permissions("dcim.add_module")
2016
+ self.add_permissions("dcim.add_module", "dcim.view_location", "dcim.view_moduletype", "extras.view_status")
1985
2017
  data = {
1986
2018
  "module_type": self.module_type.pk,
1987
2019
  "location": self.location.pk,
@@ -2006,7 +2038,7 @@ class ModuleTestCase(APIViewTestCases.APIViewTestCase):
2006
2038
  )
2007
2039
 
2008
2040
  def test_asset_tag_unique_validation(self):
2009
- self.add_permissions("dcim.add_module")
2041
+ self.add_permissions("dcim.add_module", "dcim.view_location", "dcim.view_moduletype", "extras.view_status")
2010
2042
  data = {
2011
2043
  "module_type": self.module_type.pk,
2012
2044
  "location": self.location.pk,
@@ -2321,7 +2353,7 @@ class InterfaceTest(Mixins.ModularDeviceComponentMixin, Mixins.BasePortTestMixin
2321
2353
 
2322
2354
  def test_untagged_vlan_requires_mode(self):
2323
2355
  """Test that when an `untagged_vlan` is specified, `mode` is also required."""
2324
- self.add_permissions("dcim.add_interface")
2356
+ self.add_permissions("dcim.add_interface", "dcim.view_device", "extras.view_status", "ipam.view_vlan")
2325
2357
 
2326
2358
  # This will fail.
2327
2359
  url = self._get_list_url()
@@ -2336,7 +2368,9 @@ class InterfaceTest(Mixins.ModularDeviceComponentMixin, Mixins.BasePortTestMixin
2336
2368
  )
2337
2369
 
2338
2370
  def test_tagged_vlan_must_be_in_the_location_or_parent_locations_of_the_parent_device(self):
2339
- self.add_permissions("dcim.add_interface")
2371
+ self.add_permissions(
2372
+ "dcim.add_interface", "dcim.view_interface", "dcim.view_device", "extras.view_status", "ipam.view_vlan"
2373
+ )
2340
2374
 
2341
2375
  interface_status = Status.objects.get_for_model(Interface).first()
2342
2376
  location = self.devices[0].location
@@ -2365,7 +2399,7 @@ class InterfaceTest(Mixins.ModularDeviceComponentMixin, Mixins.BasePortTestMixin
2365
2399
 
2366
2400
  def test_interface_belonging_to_common_device_or_vc_allowed(self):
2367
2401
  """Test parent, bridge, and LAG interfaces belonging to common device or VC is valid"""
2368
- self.add_permissions("dcim.add_interface")
2402
+ self.add_permissions("dcim.add_interface", "dcim.view_device", "dcim.view_interface", "extras.view_status")
2369
2403
 
2370
2404
  response = self.client.post(
2371
2405
  self._get_list_url(), data=self.common_device_or_vc_data[0], format="json", **self.header
@@ -2388,7 +2422,9 @@ class InterfaceTest(Mixins.ModularDeviceComponentMixin, Mixins.BasePortTestMixin
2388
2422
  def test_interface_not_belonging_to_common_device_or_vc_not_allowed(self):
2389
2423
  """Test parent, bridge, and LAG interfaces not belonging to common device or VC is invalid"""
2390
2424
 
2391
- self.add_permissions("dcim.add_interface")
2425
+ self.add_permissions(
2426
+ "dcim.add_interface", "dcim.view_device", "dcim.view_interface", "extras.view_status", "extras.view_role"
2427
+ )
2392
2428
 
2393
2429
  for name, payload in self.interfaces_not_belonging_to_same_device_data:
2394
2430
  response = self.client.post(self._get_list_url(), data=payload, format="json", **self.header)
@@ -2405,7 +2441,9 @@ class InterfaceTest(Mixins.ModularDeviceComponentMixin, Mixins.BasePortTestMixin
2405
2441
  )
2406
2442
 
2407
2443
  def test_tagged_vlan_raise_error_if_mode_not_set_to_tagged(self):
2408
- self.add_permissions("dcim.add_interface", "dcim.change_interface")
2444
+ self.add_permissions(
2445
+ "dcim.add_interface", "dcim.change_interface", "dcim.view_device", "extras.view_status", "ipam.view_vlan"
2446
+ )
2409
2447
  with self.subTest("On create, assert 400 status."):
2410
2448
  payload = {
2411
2449
  "device": self.devices[0].pk,
@@ -2509,7 +2547,7 @@ class FrontPortTest(Mixins.BasePortTestMixin):
2509
2547
  def test_module_device_validation(self):
2510
2548
  """Assert that a modular component can have a module or a device but not both."""
2511
2549
 
2512
- self.add_permissions("dcim.add_frontport")
2550
+ self.add_permissions("dcim.add_frontport", "dcim.view_device", "dcim.view_module", "dcim.view_rearport")
2513
2551
  data = {
2514
2552
  "module": self.module.pk,
2515
2553
  "device": self.device.pk,
@@ -2546,7 +2584,7 @@ class FrontPortTest(Mixins.BasePortTestMixin):
2546
2584
  def test_module_device_name_unique_validation(self):
2547
2585
  """Assert uniqueness constraint is enforced for (device,name) and (module,name) fields."""
2548
2586
 
2549
- self.add_permissions("dcim.add_frontport")
2587
+ self.add_permissions("dcim.add_frontport", "dcim.view_module", "dcim.view_rearport", "dcim.view_device")
2550
2588
  data = {
2551
2589
  "module": self.module.pk,
2552
2590
  "name": "test modular device component parent validation",
@@ -3410,22 +3448,26 @@ class SoftwareImageFileTestCase(Mixins.SoftwareImageFileRelatedModelMixin, APIVi
3410
3448
  def setUpTestData(cls):
3411
3449
  statuses = Status.objects.get_for_model(SoftwareImageFile)
3412
3450
  software_versions = SoftwareVersion.objects.all()
3451
+ external_integrations = ExternalIntegration.objects.all()
3413
3452
 
3414
3453
  cls.create_data = [
3415
3454
  {
3416
3455
  "software_version": software_versions[0].pk,
3417
3456
  "status": statuses[0].pk,
3418
3457
  "image_file_name": "software_image_file_test_case_1.bin",
3458
+ "external_integration": external_integrations[0].pk,
3419
3459
  },
3420
3460
  {
3421
3461
  "software_version": software_versions[1].pk,
3422
3462
  "status": statuses[1].pk,
3423
3463
  "image_file_name": "software_image_file_test_case_2.bin",
3464
+ "external_integration": external_integrations[1].pk,
3424
3465
  },
3425
3466
  {
3426
3467
  "software_version": software_versions[2].pk,
3427
3468
  "status": statuses[2].pk,
3428
3469
  "image_file_name": "software_image_file_test_case_3.bin",
3470
+ "external_integration": None,
3429
3471
  },
3430
3472
  ]
3431
3473
  cls.bulk_update_data = {
@@ -3435,6 +3477,7 @@ class SoftwareImageFileTestCase(Mixins.SoftwareImageFileRelatedModelMixin, APIVi
3435
3477
  "hashing_algorithm": SoftwareImageFileHashingAlgorithmChoices.SHA512,
3436
3478
  "image_file_size": 1234567890,
3437
3479
  "download_url": "https://example.com/software_image_file_test_case.bin",
3480
+ "external_integration": external_integrations[0].pk,
3438
3481
  }
3439
3482
 
3440
3483
 
@@ -3642,7 +3685,7 @@ class VirtualDeviceContextTestCase(APIViewTestCases.APIViewTestCase):
3642
3685
  Validate we can set primary_ip on a Virtual Device Context using a PATCH.
3643
3686
  """
3644
3687
  # Add object-level permission
3645
- self.add_permissions("dcim.change_virtualdevicecontext")
3688
+ self.add_permissions("dcim.change_virtualdevicecontext", "ipam.view_ipaddress")
3646
3689
  vdc = VirtualDeviceContext.objects.first()
3647
3690
  device = vdc.device
3648
3691
  intf_status = Status.objects.get_for_model(Interface).first()
@@ -3679,7 +3722,7 @@ class VirtualDeviceContextTestCase(APIViewTestCases.APIViewTestCase):
3679
3722
  """
3680
3723
  Validate that changing device on the virutal device context is not allowed.
3681
3724
  """
3682
- self.add_permissions("dcim.change_virtualdevicecontext")
3725
+ self.add_permissions("dcim.change_virtualdevicecontext", "dcim.view_device")
3683
3726
  vdc = VirtualDeviceContext.objects.first()
3684
3727
  old_device = vdc.device
3685
3728
  new_device = Device.objects.exclude(pk=old_device.pk).first()
@@ -3861,6 +3861,8 @@ class SoftwareImageFileFilterSetTestCase(FilterTestCases.FilterTestCase):
3861
3861
  ["software_version", "software_version__version"],
3862
3862
  ["status", "status__id"],
3863
3863
  ["status", "status__name"],
3864
+ ["external_integration", "external_integration__id"],
3865
+ ["external_integration", "external_integration__name"],
3864
3866
  )
3865
3867
 
3866
3868
  @classmethod
@@ -9,6 +9,7 @@ from django.test import TestCase
9
9
  from django.test.utils import override_settings
10
10
 
11
11
  from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider, ProviderNetwork
12
+ from nautobot.core import settings
12
13
  from nautobot.core.testing.models import ModelTestCases
13
14
  from nautobot.dcim.choices import (
14
15
  CableStatusChoices,
@@ -1337,6 +1338,19 @@ class LocationTestCase(ModelTestCases.BaseModelTestCase):
1337
1338
  str(cm.exception),
1338
1339
  )
1339
1340
 
1341
+ def test_default_treemodel_display(self):
1342
+ location_1 = Location(name="Building 1", location_type=self.root_type, status=self.status)
1343
+ location_1.validated_save()
1344
+ location_2 = Location(name="Room 1", location_type=self.leaf_type, parent=location_1, status=self.status)
1345
+ self.assertEqual(location_2.display, "Building 1 → Room 1")
1346
+
1347
+ @override_settings(LOCATION_NAME_AS_NATURAL_KEY=True)
1348
+ def test_location_name_as_natural_key_display(self):
1349
+ location_1 = Location(name="Building 1", location_type=self.root_type, status=self.status)
1350
+ location_1.validated_save()
1351
+ location_2 = Location(name="Room 1", location_type=self.leaf_type, parent=location_1, status=self.status)
1352
+ self.assertEqual(location_2.display, "Room 1")
1353
+
1340
1354
 
1341
1355
  class PlatformTestCase(TestCase):
1342
1356
  def setUp(self):
@@ -2610,6 +2624,70 @@ class InterfaceTestCase(ModularDeviceComponentTestCaseMixin, ModelTestCases.Base
2610
2624
  class SoftwareImageFileTestCase(ModelTestCases.BaseModelTestCase):
2611
2625
  model = SoftwareImageFile
2612
2626
 
2627
+ def test_download_url_validation_behaviour(self):
2628
+ """
2629
+ Test that the `download_url` property behaves as expected in relation to laxURLField validation and
2630
+ the ALLOWED_URL_SCHEMES setting.
2631
+ """
2632
+ # Prepare prerequisite objects
2633
+ platform = Platform.objects.first()
2634
+ software_version_status = Status.objects.get_for_model(SoftwareVersion).first()
2635
+ software_image_file_status = Status.objects.get_for_model(SoftwareImageFile).first()
2636
+ software_version = SoftwareVersion.objects.create(
2637
+ platform=platform, version="Test version 1.0.0", status=software_version_status
2638
+ )
2639
+
2640
+ # Test that the download_url field is correctly validated with the default ALLOWED_URL_SCHEMES setting
2641
+ for scheme in settings.ALLOWED_URL_SCHEMES:
2642
+ software_image = SoftwareImageFile(
2643
+ software_version=software_version,
2644
+ image_file_name=f"software_image_file_qs_test_{scheme}.bin",
2645
+ status=software_image_file_status,
2646
+ download_url=f"{scheme}://example.com/software_image_file_qs_test_1.bin",
2647
+ )
2648
+ try:
2649
+ software_image.validated_save()
2650
+ except ValidationError as e:
2651
+ self.fail(f"download_url Scheme {scheme} ValidationError: {e}")
2652
+
2653
+ INVALID_SCHEMES = ["httpx", "rdp", "cryptoboi"]
2654
+ OVERRIDE_VALID_SCHEMES = ["sftp", "tftp", "https", "http", "newfs", "customfs"]
2655
+ # Invalid schemes should raise a ValidationError
2656
+ for scheme in INVALID_SCHEMES:
2657
+ software_image = SoftwareImageFile(
2658
+ software_version=software_version,
2659
+ image_file_name=f"software_image_file_qs_test_{scheme}2.bin",
2660
+ status=software_image_file_status,
2661
+ download_url=f"{scheme}://example.com/software_image_file_qs_test_2.bin",
2662
+ )
2663
+ with self.assertRaises(ValidationError) as err:
2664
+ software_image.validated_save()
2665
+ self.assertEqual(err.exception.message_dict["download_url"][0], "Enter a valid URL.")
2666
+
2667
+ with override_settings(ALLOWED_URL_SCHEMES=OVERRIDE_VALID_SCHEMES):
2668
+ for scheme in OVERRIDE_VALID_SCHEMES:
2669
+ software_image = SoftwareImageFile(
2670
+ software_version=software_version,
2671
+ image_file_name=f"software_image_file_qs_test_{scheme}3.bin",
2672
+ status=software_image_file_status,
2673
+ download_url=f"{scheme}://example.com/software_image_file_qs_test_3.bin",
2674
+ )
2675
+ try:
2676
+ software_image.validated_save()
2677
+ except ValidationError as e:
2678
+ self.fail(f"download_url Scheme {scheme} ValidationError: {e}")
2679
+
2680
+ for scheme in INVALID_SCHEMES:
2681
+ software_image = SoftwareImageFile(
2682
+ software_version=software_version,
2683
+ image_file_name=f"software_image_file_qs_test_{scheme}4.bin",
2684
+ status=software_image_file_status,
2685
+ download_url=f"{scheme}://example.com/software_image_file_qs_test_4.bin",
2686
+ )
2687
+ with self.assertRaises(ValidationError) as err:
2688
+ software_image.validated_save()
2689
+ self.assertEqual(err.exception.message_dict["download_url"][0], "Enter a valid URL.")
2690
+
2613
2691
  def test_queryset_get_for_object(self):
2614
2692
  """
2615
2693
  Test that the queryset get_for_object method returns the expected results for Device, DeviceType, InventoryItem and VirtualMachine
@@ -760,7 +760,7 @@ class DeviceFamilyTestCase(ViewTestCases.PrimaryObjectViewTestCase):
760
760
  DeviceFamily.objects.create(name="Deletable Device Family 3")
761
761
 
762
762
 
763
- class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
763
+ class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase, ViewTestCases.BulkEditObjectsViewTestCase):
764
764
  model = Manufacturer
765
765
 
766
766
  @classmethod
@@ -769,6 +769,9 @@ class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
769
769
  "name": "Manufacturer X",
770
770
  "description": "A new manufacturer",
771
771
  }
772
+ cls.bulk_edit_data = {
773
+ "description": "Updated manufacturer description",
774
+ }
772
775
 
773
776
  def get_deletable_object(self):
774
777
  mf = Manufacturer.objects.create(name="Deletable Manufacturer")
@@ -4579,6 +4582,7 @@ class SoftwareImageFileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4579
4582
  device_types = DeviceType.objects.all()[:2]
4580
4583
  statuses = Status.objects.get_for_model(SoftwareImageFile)
4581
4584
  software_versions = SoftwareVersion.objects.all()
4585
+ external_integration = ExternalIntegration.objects.first()
4582
4586
 
4583
4587
  cls.form_data = {
4584
4588
  "software_version": software_versions[0].pk,
@@ -4588,6 +4592,7 @@ class SoftwareImageFileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4588
4592
  "image_file_size": 1234567890,
4589
4593
  "hashing_algorithm": SoftwareImageFileHashingAlgorithmChoices.SHA512,
4590
4594
  "download_url": "https://example.com/software_image_file_test_case.bin",
4595
+ "external_integration": external_integration.pk,
4591
4596
  "device_types": [device_types[0].pk, device_types[1].pk],
4592
4597
  }
4593
4598
 
@@ -4598,6 +4603,7 @@ class SoftwareImageFileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4598
4603
  "hashing_algorithm": SoftwareImageFileHashingAlgorithmChoices.SHA512,
4599
4604
  "image_file_size": 1234567890,
4600
4605
  "download_url": "https://example.com/software_image_file_test_case.bin",
4606
+ "external_integration": external_integration.pk,
4601
4607
  }
4602
4608
 
4603
4609
 
nautobot/dcim/urls.py CHANGED
@@ -17,7 +17,6 @@ from .models import (
17
17
  Interface,
18
18
  InventoryItem,
19
19
  Location,
20
- Manufacturer,
21
20
  Platform,
22
21
  PowerFeed,
23
22
  PowerOutlet,
@@ -40,6 +39,7 @@ router.register("device-redundancy-groups", views.DeviceRedundancyGroupUIViewSet
40
39
  router.register("interface-redundancy-groups", views.InterfaceRedundancyGroupUIViewSet)
41
40
  router.register("interface-redundancy-groups-associations", views.InterfaceRedundancyGroupAssociationUIViewSet)
42
41
  router.register("location-types", views.LocationTypeUIViewSet)
42
+ router.register("manufacturers", views.ManufacturerUIViewSet)
43
43
  router.register("module-bays", views.ModuleBayUIViewSet)
44
44
  router.register("module-bay-templates", views.ModuleBayTemplateUIViewSet)
45
45
  router.register("modules", views.ModuleUIViewSet)
@@ -208,50 +208,6 @@ urlpatterns = [
208
208
  name="rack_add_image",
209
209
  kwargs={"model": Rack},
210
210
  ),
211
- # Manufacturers
212
- path("manufacturers/", views.ManufacturerListView.as_view(), name="manufacturer_list"),
213
- path(
214
- "manufacturers/add/",
215
- views.ManufacturerEditView.as_view(),
216
- name="manufacturer_add",
217
- ),
218
- path(
219
- "manufacturers/import/",
220
- views.ManufacturerBulkImportView.as_view(), # 3.0 TODO: remove, unused
221
- name="manufacturer_import",
222
- ),
223
- path(
224
- "manufacturers/delete/",
225
- views.ManufacturerBulkDeleteView.as_view(),
226
- name="manufacturer_bulk_delete",
227
- ),
228
- path(
229
- "manufacturers/<uuid:pk>/",
230
- views.ManufacturerView.as_view(),
231
- name="manufacturer",
232
- ),
233
- path(
234
- "manufacturers/<uuid:pk>/edit/",
235
- views.ManufacturerEditView.as_view(),
236
- name="manufacturer_edit",
237
- ),
238
- path(
239
- "manufacturers/<uuid:pk>/delete/",
240
- views.ManufacturerDeleteView.as_view(),
241
- name="manufacturer_delete",
242
- ),
243
- path(
244
- "manufacturers/<uuid:pk>/changelog/",
245
- ObjectChangeLogView.as_view(),
246
- name="manufacturer_changelog",
247
- kwargs={"model": Manufacturer},
248
- ),
249
- path(
250
- "manufacturers/<uuid:pk>/notes/",
251
- ObjectNotesView.as_view(),
252
- name="manufacturer_notes",
253
- kwargs={"model": Manufacturer},
254
- ),
255
211
  # Device types
256
212
  path("device-types/", views.DeviceTypeListView.as_view(), name="devicetype_list"),
257
213
  path("device-types/add/", views.DeviceTypeEditView.as_view(), name="devicetype_add"),