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
@@ -1,38 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
- {% block content_left_page %}
5
- <div class="panel panel-default">
6
- <div class="panel-heading">
7
- <strong>Status</strong>
8
- </div>
9
- <table class="table table-hover panel-body attr-table">
10
- <tr>
11
- <td>Name</td>
12
- <td>
13
- {{ object.name }}
14
- </td>
15
- </tr>
16
- <tr>
17
- <td>Content Type(s)</td>
18
- <td>
19
- {% for ct in content_types %}
20
- {{ ct }}<br>
21
- {% endfor %}
22
- </td>
23
- </tr>
24
- <tr>
25
- <td>Color</td>
26
- <td>
27
- <span class="label color-block" style="background-color: #{{ object.color }}">&nbsp;</span>
28
- </td>
29
- </tr>
30
- <tr>
31
- <td>Description</td>
32
- <td>
33
- {{ object.description|placeholder }}
34
- </td>
35
- </tr>
36
- </table>
37
- </div>
38
- {% endblock content_left_page %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1,59 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
- {% block content_left_page %}
5
- <div class="panel panel-default">
6
- <div class="panel-heading">
7
- <strong>Team</strong>
8
- </div>
9
- <table class="table table-hover panel-body attr-table">
10
- <tr>
11
- <td>Name</td>
12
- <td>{{ object.name }}</td>
13
- </tr>
14
- <tr>
15
- <td>Phone</td>
16
- <td>{{ object.phone|hyperlinked_phone_number }}</td>
17
- </tr>
18
- <tr>
19
- <td>E-mail</td>
20
- <td>{{ object.email|hyperlinked_email }}</td>
21
- </tr>
22
- <tr>
23
- <td>Address</td>
24
- <td>
25
- {% if object.address %}
26
- <div class="pull-right noprint">
27
- <a href="https://maps.google.com/?q={{ object.address|urlencode }}" target="_blank" class="btn btn-primary btn-xs">
28
- <i class="mdi mdi-map-marker"></i> Map it
29
- </a>
30
- </div>
31
- <span>{{ object.address|linebreaksbr }}</span>
32
- {% else %}
33
- <span class="text-muted">&mdash;</span>
34
- {% endif %}
35
- </td>
36
- </tr>
37
- </table>
38
- </div>
39
- <div class="panel panel-default">
40
- <div class="panel-heading">
41
- <strong>Comments</strong>
42
- </div>
43
- <div class="panel-body rendered-markdown">
44
- {% if object.comments %}
45
- {{ object.comments|render_markdown }}
46
- {% else %}
47
- <span class="text-muted">None</span>
48
- {% endif %}
49
- </div>
50
- </div>
51
- {% endblock %}
52
-
53
- {% block content_right_page %}
54
- {% include 'panel_table.html' with table=contacts_table heading='Assigned Contacts' %}
55
- {% endblock %}
56
-
57
- {% block content_full_width_page %}
58
- {% include 'panel_table.html' with table=contact_associations_table heading='Contact For' %}
59
- {% endblock %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -42,4 +42,4 @@ class NoteTestCase(SeleniumTestCase):
42
42
  self.browser.find_by_text("Create").click()
43
43
 
44
44
  # Verify form redirect and presence of content.
45
- self.assertTrue(self.browser.is_text_present("Created Note"))
45
+ self.assertTrue(self.browser.is_text_present("Created note"))
@@ -292,7 +292,7 @@ class ConfigContextTest(APIViewTestCases.APIViewTestCase):
292
292
  schema = ConfigContextSchema.objects.create(
293
293
  name="Schema 1", data_schema={"type": "object", "properties": {"foo": {"type": "string"}}}
294
294
  )
295
- self.add_permissions("extras.add_configcontext")
295
+ self.add_permissions("extras.add_configcontext", "extras.view_configcontextschema")
296
296
 
297
297
  data = {
298
298
  "name": "Config Context with schema",
@@ -2248,7 +2248,7 @@ class JobHookTest(APIViewTestCases.APIViewTestCase):
2248
2248
  "type_delete": True,
2249
2249
  }
2250
2250
 
2251
- self.add_permissions("extras.add_jobhook")
2251
+ self.add_permissions("extras.add_jobhook", "extras.view_job")
2252
2252
  response = self.client.post(self._get_list_url(), data, format="json", **self.header)
2253
2253
  self.assertContains(
2254
2254
  response,
@@ -2264,7 +2264,7 @@ class JobHookTest(APIViewTestCases.APIViewTestCase):
2264
2264
  "type_delete": True,
2265
2265
  }
2266
2266
 
2267
- self.add_permissions("extras.change_jobhook")
2267
+ self.add_permissions("extras.change_jobhook", "extras.view_job")
2268
2268
  job_hook2 = JobHook.objects.get(name="JobHook2")
2269
2269
  response = self.client.patch(self._get_detail_url(job_hook2), data, format="json", **self.header)
2270
2270
  self.assertContains(
@@ -2553,7 +2553,7 @@ class UserSavedViewAssociationTest(APIViewTestCases.APIViewTestCase):
2553
2553
  "saved_view": saved_view.pk,
2554
2554
  "view_name": duplicate_view_name,
2555
2555
  }
2556
- self.add_permissions("extras.add_usersavedviewassociation")
2556
+ self.add_permissions("extras.add_usersavedviewassociation", "users.view_user", "extras.view_savedview")
2557
2557
  response = self.client.post(
2558
2558
  self._get_list_url(), duplicate_user_to_savedview_create_data, format="json", **self.header
2559
2559
  )
@@ -3240,7 +3240,15 @@ class RelationshipTest(APIViewTestCases.APIViewTestCase, RequiredRelationshipTes
3240
3240
  location=existing_location_2,
3241
3241
  )
3242
3242
 
3243
- self.add_permissions("dcim.view_location", "dcim.add_location", "extras.add_relationshipassociation")
3243
+ self.add_permissions(
3244
+ "dcim.view_location",
3245
+ "dcim.view_locationtype",
3246
+ "dcim.view_device",
3247
+ "dcim.add_location",
3248
+ "extras.view_relationship",
3249
+ "extras.add_relationshipassociation",
3250
+ "extras.view_status",
3251
+ )
3244
3252
  response = self.client.post(
3245
3253
  reverse("dcim-api:location-list"),
3246
3254
  data={
@@ -3564,7 +3572,9 @@ class RelationshipAssociationTest(APIViewTestCases.APIViewTestCase):
3564
3572
  ),
3565
3573
  ]
3566
3574
 
3567
- self.add_permissions("extras.add_relationshipassociation")
3575
+ self.add_permissions(
3576
+ "extras.add_relationshipassociation", "dcim.view_device", "dcim.view_location", "extras.view_relationship"
3577
+ )
3568
3578
 
3569
3579
  for side, field_error_name, data in associations:
3570
3580
  response = self.client.post(self._get_list_url(), data, format="json", **self.header)
@@ -3585,7 +3595,9 @@ class RelationshipAssociationTest(APIViewTestCases.APIViewTestCase):
3585
3595
  "destination_id": self.devices[2].pk,
3586
3596
  }
3587
3597
 
3588
- self.add_permissions("extras.add_relationshipassociation")
3598
+ self.add_permissions(
3599
+ "extras.add_relationshipassociation", "extras.view_relationship", "dcim.view_device", "dcim.view_location"
3600
+ )
3589
3601
 
3590
3602
  response = self.client.post(self._get_list_url(), data, format="json", **self.header)
3591
3603
  self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
@@ -3636,8 +3648,11 @@ class RelationshipAssociationTest(APIViewTestCases.APIViewTestCase):
3636
3648
  Check that relationship-associations can be updated via the 'relationships' field.
3637
3649
  """
3638
3650
  self.add_permissions(
3651
+ "dcim.view_device",
3639
3652
  "dcim.view_location",
3640
3653
  "dcim.change_location",
3654
+ "extras.view_relationship",
3655
+ "extras.view_relationshipassociation",
3641
3656
  "extras.add_relationshipassociation",
3642
3657
  "extras.delete_relationshipassociation",
3643
3658
  )
@@ -277,7 +277,7 @@ class ChangeLogAPITest(APITestCase):
277
277
  ],
278
278
  }
279
279
  url = reverse("dcim-api:location-list")
280
- self.add_permissions("dcim.add_location", "extras.view_status")
280
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_tag", "extras.view_status")
281
281
 
282
282
  response = self.client.post(url, data, format="json", **self.header)
283
283
  self.assertHttpStatus(response, status.HTTP_201_CREATED)
@@ -310,7 +310,7 @@ class ChangeLogAPITest(APITestCase):
310
310
  },
311
311
  "tags": [{"name": self.tags[2].name}],
312
312
  }
313
- self.add_permissions("dcim.change_location", "extras.view_status")
313
+ self.add_permissions("dcim.change_location", "extras.view_status", "dcim.view_locationtype", "extras.view_tag")
314
314
  url = reverse("dcim-api:location-detail", kwargs={"pk": location.pk})
315
315
 
316
316
  response = self.client.put(url, data, format="json", **self.header)
@@ -457,7 +457,7 @@ class ChangeLogAPITest(APITestCase):
457
457
  "status": self.statuses[0].pk,
458
458
  "location_type": location_type.pk,
459
459
  }
460
- self.add_permissions("dcim.add_location")
460
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_status")
461
461
  url = reverse("dcim-api:location-list")
462
462
 
463
463
  response = self.client.post(url, location_payload, format="json", **self.header)
@@ -492,7 +492,7 @@ class ChangeLogAPITest(APITestCase):
492
492
  )
493
493
 
494
494
  payload = {"tagged_vlans": [str(tagged_vlan.pk)], "description": "test vm interface m2m change"}
495
- self.add_permissions("virtualization.change_vminterface", "ipam.change_vlan")
495
+ self.add_permissions("virtualization.change_vminterface", "ipam.change_vlan", "ipam.view_vlan")
496
496
  url = reverse("virtualization-api:vminterface-detail", kwargs={"pk": vm_interface.pk})
497
497
  response = self.client.patch(url, payload, format="json", **self.header)
498
498
  vm_interface.refresh_from_db()
@@ -472,6 +472,9 @@ class CustomFieldDataAPITest(APITestCase):
472
472
  "dcim.add_location",
473
473
  "dcim.change_location",
474
474
  "dcim.view_location",
475
+ "dcim.view_locationtype",
476
+ "extras.view_status",
477
+ "extras.view_customfield",
475
478
  )
476
479
 
477
480
  def setUp(self):
@@ -2080,6 +2083,30 @@ class CustomFieldBackgroundTasks(TransactionTestCase):
2080
2083
  self.assertEqual(oc_list[0].change_context_detail, "delete custom field data")
2081
2084
  self.assertEqual(oc_list[0].user, self.user)
2082
2085
 
2086
+ def test_clear_custom_field_data_task(self):
2087
+ obj_type = ContentType.objects.get_for_model(Location)
2088
+ cf_1 = CustomField.objects.create(label="CF1", type=CustomFieldTypeChoices.TYPE_TEXT)
2089
+ cf_1.content_types.set([obj_type])
2090
+ location_type = LocationType.objects.create(name="Root Type 2")
2091
+ location_status = Status.objects.get_for_model(Location).first()
2092
+ location = Location.objects.create(
2093
+ name="Location 1",
2094
+ location_type=location_type,
2095
+ status=location_status,
2096
+ _custom_field_data={"cf1": "foo"},
2097
+ )
2098
+
2099
+ with web_request_context(self.user):
2100
+ cf_1.content_types.clear()
2101
+ location.refresh_from_db()
2102
+ self.assertNotIn("cf1", location.cf)
2103
+
2104
+ oc_list = get_changes_for_model(location)
2105
+ self.assertEqual(len(oc_list), 1)
2106
+ self.assertEqual(oc_list[0].changed_object, location)
2107
+ self.assertEqual(oc_list[0].change_context_detail, "delete custom field data from existing content types")
2108
+ self.assertEqual(oc_list[0].user, self.user)
2109
+
2083
2110
  def test_update_custom_field_choice_data_task(self):
2084
2111
  obj_type = ContentType.objects.get_for_model(Location)
2085
2112
  cf = CustomField(
@@ -486,7 +486,7 @@ class TestUserContextCustomValidator(CustomValidator):
486
486
  """
487
487
  Used to validate that the correct user context is available in the custom validator.
488
488
  """
489
- self.validation_error(self.context["user"])
489
+ self.validation_error(f"TestUserContextCustomValidator: user is {self.context['user']}")
490
490
 
491
491
 
492
492
  class AppCustomValidationTest(TestCase):
@@ -527,22 +527,28 @@ class AppCustomValidationTest(TestCase):
527
527
 
528
528
  def test_custom_validator_non_web_request_uses_anonymous_user(self):
529
529
  location_type = LocationType.objects.get(name="Campus")
530
- registry["plugin_custom_validators"]["dcim.locationtype"] = [TestUserContextCustomValidator]
530
+ before = registry["plugin_custom_validators"]["dcim.locationtype"]
531
+ try:
532
+ registry["plugin_custom_validators"]["dcim.locationtype"] = [TestUserContextCustomValidator]
531
533
 
532
- from django.contrib.auth.models import AnonymousUser
533
-
534
- with self.assertRaises(ValidationError) as context:
535
- location_type.clean()
536
- self.assertEqual(context.exception.message, AnonymousUser())
534
+ with self.assertRaises(ValidationError) as context:
535
+ location_type.clean()
536
+ self.assertEqual(context.exception.message, "TestUserContextCustomValidator: user is AnonymousUser")
537
+ finally:
538
+ registry["plugin_custom_validators"]["dcim.locationtype"] = before
537
539
 
538
540
  def test_custom_validator_web_request_uses_real_user(self):
539
541
  location_type = LocationType.objects.get(name="Campus")
540
- registry["plugin_custom_validators"]["dcim.locationtype"] = [TestUserContextCustomValidator]
541
-
542
- with self.assertRaises(ValidationError) as context:
543
- with web_request_context(user=self.user):
544
- location_type.clean()
545
- self.assertEqual(context.exception.message, self.user)
542
+ before = registry["plugin_custom_validators"]["dcim.locationtype"]
543
+ try:
544
+ registry["plugin_custom_validators"]["dcim.locationtype"] = [TestUserContextCustomValidator]
545
+
546
+ with self.assertRaises(ValidationError) as context:
547
+ with web_request_context(user=self.user):
548
+ location_type.clean()
549
+ self.assertEqual(context.exception.message, f"TestUserContextCustomValidator: user is {self.user}")
550
+ finally:
551
+ registry["plugin_custom_validators"]["dcim.locationtype"] = before
546
552
 
547
553
 
548
554
  class ExampleModelCustomActionViewTest(TestCase):
@@ -1,10 +1,13 @@
1
+ import contextlib
1
2
  import logging
2
3
  import uuid
3
4
 
4
5
  from django.contrib.contenttypes.models import ContentType
6
+ from django.core.cache import cache
5
7
  from django.core.exceptions import ValidationError
6
8
  from django.urls import reverse
7
9
  from django.utils.html import format_html
10
+ import redis.exceptions
8
11
 
9
12
  from nautobot.circuits.models import CircuitType
10
13
  from nautobot.core.forms import (
@@ -179,6 +182,12 @@ class RelationshipBaseTest:
179
182
  ),
180
183
  ]
181
184
 
185
+ def tearDown(self):
186
+ """Ensure that relationship caches are cleared to avoid leakage into other tests."""
187
+ with contextlib.suppress(redis.exceptions.ConnectionError):
188
+ cache.delete_pattern(f"{Relationship.objects.get_for_model_source.cache_key_prefix}.*")
189
+ cache.delete_pattern(f"{Relationship.objects.get_for_model_destination.cache_key_prefix}.*")
190
+
182
191
 
183
192
  class RelationshipTest(RelationshipBaseTest, ModelTestCases.BaseModelTestCase):
184
193
  model = Relationship
@@ -46,7 +46,7 @@ class TaggedItemTest(APITestCase):
46
46
  "location_type": self.location_type.pk,
47
47
  }
48
48
  url = reverse("dcim-api:location-list")
49
- self.add_permissions("dcim.add_location")
49
+ self.add_permissions("dcim.add_location", "dcim.view_locationtype", "extras.view_tag", "extras.view_status")
50
50
 
51
51
  response = self.client.post(url, data, format="json", **self.header)
52
52
  self.assertHttpStatus(response, status.HTTP_201_CREATED)
@@ -67,7 +67,7 @@ class TaggedItemTest(APITestCase):
67
67
  {"name": self.tags[3].name},
68
68
  ]
69
69
  }
70
- self.add_permissions("dcim.change_location")
70
+ self.add_permissions("dcim.change_location", "extras.view_tag")
71
71
  url = reverse("dcim-api:location-detail", kwargs={"pk": location.pk})
72
72
 
73
73
  response = self.client.patch(url, data, format="json", **self.header)
@@ -1069,12 +1069,14 @@ class ExportTemplateTestCase(
1069
1069
  ViewTestCases.GetObjectViewTestCase,
1070
1070
  ViewTestCases.GetObjectChangelogViewTestCase,
1071
1071
  ViewTestCases.ListObjectsViewTestCase,
1072
+ ViewTestCases.BulkEditObjectsViewTestCase,
1072
1073
  ):
1073
1074
  model = ExportTemplate
1074
1075
 
1075
1076
  @classmethod
1076
1077
  def setUpTestData(cls):
1077
1078
  obj_type = ContentType.objects.get_for_model(Location)
1079
+ obj_type_1 = ContentType.objects.get_for_model(Interface)
1078
1080
 
1079
1081
  templates = (
1080
1082
  ExportTemplate(
@@ -1102,6 +1104,12 @@ class ExportTemplateTestCase(
1102
1104
  "content_type": obj_type.pk,
1103
1105
  "template_code": "template-4 test4",
1104
1106
  }
1107
+ cls.bulk_edit_data = {
1108
+ "content_type": obj_type_1.pk,
1109
+ "description": "Updated template description",
1110
+ "mime_type": "application/json",
1111
+ "file_extension": "json",
1112
+ }
1105
1113
 
1106
1114
 
1107
1115
  class ExternalIntegrationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
@@ -3376,6 +3384,7 @@ class RelationshipTestCase(
3376
3384
  ViewTestCases.GetObjectChangelogViewTestCase,
3377
3385
  ViewTestCases.ListObjectsViewTestCase,
3378
3386
  RequiredRelationshipTestMixin,
3387
+ ViewTestCases.BulkEditObjectsViewTestCase,
3379
3388
  ):
3380
3389
  model = Relationship
3381
3390
  slug_source = "label"
@@ -3422,6 +3431,19 @@ class RelationshipTestCase(
3422
3431
  "destination_hidden": True,
3423
3432
  "destination_filter": None,
3424
3433
  }
3434
+ cls.bulk_edit_data = {
3435
+ "description": "This is a relationship between VLANs and Interfaces.",
3436
+ "type": "many-to-many",
3437
+ "source_type": vlan_type.pk,
3438
+ "source_label": "Interfaces",
3439
+ "source_hidden": False,
3440
+ "source_filter": '{"status": ["' + status.name + '"]}',
3441
+ "destination_type": interface_type.pk,
3442
+ "destination_label": "VLANs",
3443
+ "destination_hidden": True,
3444
+ "destination_filter": None,
3445
+ "advanced_ui": True,
3446
+ }
3425
3447
 
3426
3448
  cls.slug_test_object = "Primary Interface"
3427
3449
 
@@ -3584,23 +3606,28 @@ class StatusTestCase(
3584
3606
  ViewTestCases.GetObjectViewTestCase,
3585
3607
  ViewTestCases.GetObjectChangelogViewTestCase,
3586
3608
  ViewTestCases.ListObjectsViewTestCase,
3609
+ ViewTestCases.BulkEditObjectsViewTestCase,
3587
3610
  ):
3588
3611
  model = Status
3589
3612
 
3590
3613
  @classmethod
3591
3614
  def setUpTestData(cls):
3592
3615
  # Status objects to test.
3593
- content_type = ContentType.objects.get_for_model(Device)
3616
+ device_ct = ContentType.objects.get_for_model(Device)
3617
+ circuit_ct = ContentType.objects.get_for_model(Circuit)
3618
+ interface_ct = ContentType.objects.get_for_model(Interface)
3594
3619
 
3595
3620
  cls.form_data = {
3596
3621
  "name": "new_status",
3597
3622
  "description": "I am a new status object.",
3598
3623
  "color": "ffcc00",
3599
- "content_types": [content_type.pk],
3624
+ "content_types": [device_ct.pk],
3600
3625
  }
3601
3626
 
3602
3627
  cls.bulk_edit_data = {
3603
3628
  "color": "000000",
3629
+ "add_content_types": [interface_ct.pk, circuit_ct.pk],
3630
+ "remove_content_types": [device_ct.pk],
3604
3631
  }
3605
3632
 
3606
3633
 
@@ -3790,25 +3817,29 @@ class WebhookTestCase(
3790
3817
  }
3791
3818
 
3792
3819
 
3793
- class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
3820
+ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase, ViewTestCases.BulkEditObjectsViewTestCase):
3794
3821
  model = Role
3795
3822
 
3796
3823
  @classmethod
3797
3824
  def setUpTestData(cls):
3798
- # Status objects to test.
3799
- content_type = ContentType.objects.get_for_model(Device)
3825
+ # Role objects to test.
3826
+ device_ct = ContentType.objects.get_for_model(Device)
3827
+ ipaddress_ct = ContentType.objects.get_for_model(IPAddress)
3828
+ prefix_ct = ContentType.objects.get_for_model(Prefix)
3800
3829
 
3801
3830
  cls.form_data = {
3802
3831
  "name": "New Role",
3803
3832
  "description": "I am a new role object.",
3804
3833
  "color": ColorChoices.COLOR_GREY,
3805
- "content_types": [content_type.pk],
3834
+ "content_types": [device_ct.pk],
3806
3835
  }
3807
3836
 
3808
3837
  cls.bulk_edit_data = {
3809
3838
  "color": "000000",
3810
3839
  "description": "I used to be a new role object.",
3811
3840
  "weight": 255,
3841
+ "add_content_types": [ipaddress_ct.pk, prefix_ct.pk],
3842
+ "remove_content_types": [device_ct.pk],
3812
3843
  }
3813
3844
 
3814
3845
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
nautobot/extras/urls.py CHANGED
@@ -9,7 +9,6 @@ from nautobot.extras.models import (
9
9
  CustomField,
10
10
  CustomLink,
11
11
  DynamicGroup,
12
- ExportTemplate,
13
12
  GitRepository,
14
13
  GraphQLQuery,
15
14
  Job,
@@ -17,7 +16,6 @@ from nautobot.extras.models import (
17
16
  Note,
18
17
  Relationship,
19
18
  SecretsGroup,
20
- Status,
21
19
  Tag,
22
20
  Webhook,
23
21
  )
@@ -27,15 +25,18 @@ app_name = "extras"
27
25
  router = NautobotUIViewSetRouter()
28
26
  router.register("contacts", views.ContactUIViewSet)
29
27
  router.register("contact-associations", views.ContactAssociationUIViewSet)
28
+ router.register("export-templates", views.ExportTemplateUIViewSet)
30
29
  router.register("external-integrations", views.ExternalIntegrationUIViewSet)
31
30
  router.register("job-buttons", views.JobButtonUIViewSet)
32
31
  router.register("job-queues", views.JobQueueUIViewSet)
33
32
  router.register("metadata-types", views.MetadataTypeUIViewSet)
34
33
  router.register("object-metadata", views.ObjectMetadataUIViewSet)
34
+ router.register("relationships", views.RelationshipUIViewSet)
35
35
  router.register("roles", views.RoleUIViewSet)
36
36
  router.register("saved-views", views.SavedViewUIViewSet)
37
37
  router.register("secrets", views.SecretUIViewSet)
38
38
  router.register("static-group-associations", views.StaticGroupAssociationUIViewSet)
39
+ router.register("statuses", views.StatusUIViewSet)
39
40
  router.register("teams", views.TeamUIViewSet)
40
41
 
41
42
  urlpatterns = [
@@ -268,49 +269,6 @@ urlpatterns = [
268
269
  name="dynamicgroup_notes",
269
270
  kwargs={"model": DynamicGroup},
270
271
  ),
271
- # Export Templates
272
- path(
273
- "export-templates/",
274
- views.ExportTemplateListView.as_view(),
275
- name="exporttemplate_list",
276
- ),
277
- path(
278
- "export-templates/add/",
279
- views.ExportTemplateEditView.as_view(),
280
- name="exporttemplate_add",
281
- ),
282
- path(
283
- "export-templates/delete/",
284
- views.ExportTemplateBulkDeleteView.as_view(),
285
- name="exporttemplate_bulk_delete",
286
- ),
287
- path(
288
- "export-templates/<uuid:pk>/",
289
- views.ExportTemplateView.as_view(),
290
- name="exporttemplate",
291
- ),
292
- path(
293
- "export-templates/<uuid:pk>/edit/",
294
- views.ExportTemplateEditView.as_view(),
295
- name="exporttemplate_edit",
296
- ),
297
- path(
298
- "export-templates/<uuid:pk>/delete/",
299
- views.ExportTemplateDeleteView.as_view(),
300
- name="exporttemplate_delete",
301
- ),
302
- path(
303
- "export-templates/<uuid:pk>/changelog/",
304
- views.ObjectChangeLogView.as_view(),
305
- name="exporttemplate_changelog",
306
- kwargs={"model": ExportTemplate},
307
- ),
308
- path(
309
- "export-templates/<uuid:pk>/notes/",
310
- views.ObjectNotesView.as_view(),
311
- name="exporttemplate_notes",
312
- kwargs={"model": ExportTemplate},
313
- ),
314
272
  # Git repositories
315
273
  path(
316
274
  "git-repositories/",
@@ -512,32 +470,6 @@ urlpatterns = [
512
470
  path("notes/<uuid:pk>/edit/", views.NoteEditView.as_view(), name="note_edit"),
513
471
  path("notes/<uuid:pk>/delete/", views.NoteDeleteView.as_view(), name="note_delete"),
514
472
  # Custom relationships
515
- path("relationships/", views.RelationshipListView.as_view(), name="relationship_list"),
516
- path(
517
- "relationships/add/",
518
- views.RelationshipEditView.as_view(),
519
- name="relationship_add",
520
- ),
521
- path(
522
- "relationships/delete/",
523
- views.RelationshipBulkDeleteView.as_view(),
524
- name="relationship_bulk_delete",
525
- ),
526
- path(
527
- "relationships/<uuid:pk>/",
528
- views.RelationshipView.as_view(),
529
- name="relationship",
530
- ),
531
- path(
532
- "relationships/<uuid:pk>/edit/",
533
- views.RelationshipEditView.as_view(),
534
- name="relationship_edit",
535
- ),
536
- path(
537
- "relationships/<uuid:pk>/delete/",
538
- views.RelationshipDeleteView.as_view(),
539
- name="relationship_delete",
540
- ),
541
473
  path(
542
474
  "relationships/<uuid:pk>/changelog/",
543
475
  views.ObjectChangeLogView.as_view(),
@@ -589,35 +521,6 @@ urlpatterns = [
589
521
  name="secretsgroup_notes",
590
522
  kwargs={"model": SecretsGroup},
591
523
  ),
592
- # Custom statuses
593
- path("statuses/", views.StatusListView.as_view(), name="status_list"),
594
- path("statuses/add/", views.StatusEditView.as_view(), name="status_add"),
595
- path("statuses/edit/", views.StatusBulkEditView.as_view(), name="status_bulk_edit"),
596
- path(
597
- "statuses/delete/",
598
- views.StatusBulkDeleteView.as_view(),
599
- name="status_bulk_delete",
600
- ),
601
- path("statuses/import/", views.StatusBulkImportView.as_view(), name="status_import"), # 3.0 TODO: remove, unused
602
- path("statuses/<uuid:pk>/", views.StatusView.as_view(), name="status"),
603
- path("statuses/<uuid:pk>/edit/", views.StatusEditView.as_view(), name="status_edit"),
604
- path(
605
- "statuses/<uuid:pk>/delete/",
606
- views.StatusDeleteView.as_view(),
607
- name="status_delete",
608
- ),
609
- path(
610
- "statuses/<uuid:pk>/changelog/",
611
- views.ObjectChangeLogView.as_view(),
612
- name="status_changelog",
613
- kwargs={"model": Status},
614
- ),
615
- path(
616
- "statuses/<uuid:pk>/notes/",
617
- views.ObjectNotesView.as_view(),
618
- name="status_notes",
619
- kwargs={"model": Status},
620
- ),
621
524
  # Tags
622
525
  path("tags/", views.TagListView.as_view(), name="tag_list"),
623
526
  path("tags/add/", views.TagEditView.as_view(), name="tag_add"),