nautobot 2.4.9__py3-none-any.whl → 2.4.11__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 (433) hide show
  1. nautobot/cloud/tests/test_views.py +13 -1
  2. nautobot/cloud/views.py +39 -9
  3. nautobot/core/api/parsers.py +56 -2
  4. nautobot/core/celery/__init__.py +21 -0
  5. nautobot/core/celery/encoders.py +3 -0
  6. nautobot/core/forms/forms.py +4 -1
  7. nautobot/core/jobs/bulk_actions.py +8 -8
  8. nautobot/core/jobs/cleanup.py +11 -0
  9. nautobot/core/management/commands/generate_test_data.py +2 -1
  10. nautobot/core/models/__init__.py +2 -0
  11. nautobot/core/templates/generic/object_retrieve.html +1 -1
  12. nautobot/core/testing/mixins.py +19 -1
  13. nautobot/core/testing/views.py +104 -8
  14. nautobot/core/tests/test_csv.py +92 -1
  15. nautobot/core/tests/test_jinja_filters.py +59 -0
  16. nautobot/core/tests/test_jobs.py +20 -4
  17. nautobot/core/tests/test_utils.py +193 -0
  18. nautobot/core/tests/test_views.py +73 -0
  19. nautobot/core/tests/test_views_utils.py +53 -2
  20. nautobot/core/ui/object_detail.py +4 -0
  21. nautobot/core/urls.py +2 -2
  22. nautobot/core/utils/lookup.py +4 -2
  23. nautobot/core/utils/module_loading.py +86 -58
  24. nautobot/core/views/__init__.py +21 -0
  25. nautobot/core/views/generic.py +2 -12
  26. nautobot/core/views/mixins.py +19 -1
  27. nautobot/core/views/renderers.py +4 -13
  28. nautobot/core/views/utils.py +16 -0
  29. nautobot/dcim/api/serializers.py +13 -0
  30. nautobot/dcim/api/urls.py +1 -0
  31. nautobot/dcim/api/views.py +20 -0
  32. nautobot/dcim/apps.py +1 -0
  33. nautobot/dcim/factory.py +11 -0
  34. nautobot/dcim/filters/__init__.py +110 -0
  35. nautobot/dcim/forms.py +205 -19
  36. nautobot/dcim/migrations/0070_modulefamily_models.py +92 -0
  37. nautobot/dcim/models/__init__.py +2 -0
  38. nautobot/dcim/models/device_component_templates.py +18 -0
  39. nautobot/dcim/models/device_components.py +25 -1
  40. nautobot/dcim/models/devices.py +68 -0
  41. nautobot/dcim/navigation.py +16 -0
  42. nautobot/dcim/tables/__init__.py +2 -0
  43. nautobot/dcim/tables/devices.py +48 -0
  44. nautobot/dcim/tables/devicetypes.py +35 -1
  45. nautobot/dcim/tables/template_code.py +2 -0
  46. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -90
  47. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
  48. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +1 -63
  49. nautobot/dcim/templates/dcim/location.html +2 -249
  50. nautobot/dcim/templates/dcim/location_edit.html +2 -38
  51. nautobot/dcim/templates/dcim/location_retrieve.html +249 -0
  52. nautobot/dcim/templates/dcim/location_update.html +38 -0
  53. nautobot/dcim/templates/dcim/module_update.html +1 -0
  54. nautobot/dcim/templates/dcim/modulebay_retrieve.html +93 -1
  55. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +31 -0
  56. nautobot/dcim/templates/dcim/moduletype_retrieve.html +6 -0
  57. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -160
  58. nautobot/dcim/tests/test_api.py +35 -0
  59. nautobot/dcim/tests/test_filters.py +102 -3
  60. nautobot/dcim/tests/test_models.py +146 -0
  61. nautobot/dcim/tests/test_views.py +70 -97
  62. nautobot/dcim/urls.py +4 -22
  63. nautobot/dcim/views.py +439 -153
  64. nautobot/extras/api/views.py +9 -2
  65. nautobot/extras/context_managers.py +2 -2
  66. nautobot/extras/datasources/git.py +11 -3
  67. nautobot/extras/forms/forms.py +9 -5
  68. nautobot/extras/jobs.py +4 -2
  69. nautobot/extras/models/customfields.py +2 -0
  70. nautobot/extras/models/datasources.py +13 -8
  71. nautobot/extras/models/groups.py +18 -0
  72. nautobot/extras/models/jobs.py +19 -0
  73. nautobot/extras/models/metadata.py +2 -0
  74. nautobot/extras/models/models.py +4 -0
  75. nautobot/extras/models/secrets.py +7 -0
  76. nautobot/extras/plugins/__init__.py +3 -0
  77. nautobot/extras/secrets/__init__.py +14 -0
  78. nautobot/extras/tables.py +40 -3
  79. nautobot/extras/templates/extras/configcontext.html +2 -220
  80. nautobot/extras/templates/extras/configcontext_edit.html +2 -50
  81. nautobot/extras/templates/extras/configcontext_retrieve.html +2 -0
  82. nautobot/extras/templates/extras/configcontext_update.html +50 -0
  83. nautobot/extras/templates/extras/configcontextschema.html +2 -48
  84. nautobot/extras/templates/extras/configcontextschema_edit.html +2 -19
  85. nautobot/extras/templates/extras/configcontextschema_retrieve.html +48 -0
  86. nautobot/extras/templates/extras/configcontextschema_update.html +19 -0
  87. nautobot/extras/templates/extras/inc/configcontext_data.html +1 -0
  88. nautobot/extras/templates/extras/inc/json_data.html +1 -1
  89. nautobot/extras/templates/extras/inc/json_format.html +2 -2
  90. nautobot/extras/templates/extras/job_edit.html +12 -6
  91. nautobot/extras/templates/extras/tag.html +2 -52
  92. nautobot/extras/templates/extras/tag_edit.html +2 -15
  93. nautobot/extras/templates/extras/tag_retrieve.html +52 -0
  94. nautobot/extras/templates/extras/tag_update.html +15 -0
  95. nautobot/extras/templates/extras/team_retrieve.html +2 -2
  96. nautobot/extras/tests/test_api.py +15 -15
  97. nautobot/extras/tests/test_context_managers.py +20 -0
  98. nautobot/extras/tests/test_filters.py +4 -4
  99. nautobot/extras/tests/test_jobs.py +23 -10
  100. nautobot/extras/tests/test_models.py +45 -8
  101. nautobot/extras/tests/test_plugins.py +6 -3
  102. nautobot/extras/tests/test_views.py +66 -11
  103. nautobot/extras/urls.py +4 -134
  104. nautobot/extras/views.py +113 -158
  105. nautobot/ipam/models.py +51 -4
  106. nautobot/ipam/tables.py +19 -0
  107. nautobot/ipam/templates/ipam/vlan.html +2 -84
  108. nautobot/ipam/templates/ipam/vlan_edit.html +2 -24
  109. nautobot/ipam/templates/ipam/vlan_retrieve.html +84 -0
  110. nautobot/ipam/templates/ipam/vlan_update.html +24 -0
  111. nautobot/ipam/tests/test_views.py +5 -0
  112. nautobot/ipam/urls.py +1 -21
  113. nautobot/ipam/views.py +45 -70
  114. nautobot/project-static/docs/404.html +31 -8
  115. nautobot/project-static/docs/apps/index.html +31 -8
  116. nautobot/project-static/docs/apps/nautobot-apps.html +31 -8
  117. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +31 -8
  118. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +31 -8
  119. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +31 -8
  120. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +31 -8
  121. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +31 -8
  122. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +31 -8
  123. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +31 -8
  124. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +31 -8
  125. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +31 -8
  126. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +31 -8
  127. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +31 -8
  128. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +31 -8
  129. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +31 -8
  130. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +31 -8
  131. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +31 -8
  132. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +31 -8
  133. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +31 -8
  134. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +31 -8
  135. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +31 -8
  136. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +120 -8
  137. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +31 -8
  138. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +31 -8
  139. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +31 -8
  140. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +31 -8
  141. nautobot/project-static/docs/development/apps/api/configuration-view.html +31 -8
  142. nautobot/project-static/docs/development/apps/api/database-backend-config.html +31 -8
  143. nautobot/project-static/docs/development/apps/api/models/django-admin.html +31 -8
  144. nautobot/project-static/docs/development/apps/api/models/global-search.html +31 -8
  145. nautobot/project-static/docs/development/apps/api/models/graphql.html +31 -8
  146. nautobot/project-static/docs/development/apps/api/models/index.html +31 -8
  147. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +40 -8
  148. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +31 -8
  149. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +31 -8
  150. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +31 -8
  151. nautobot/project-static/docs/development/apps/api/platform-features/index.html +31 -8
  152. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +31 -8
  153. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +31 -8
  154. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +31 -8
  155. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +70 -46
  156. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +31 -8
  157. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +31 -8
  158. nautobot/project-static/docs/development/apps/api/prometheus.html +31 -8
  159. nautobot/project-static/docs/development/apps/api/setup.html +31 -8
  160. nautobot/project-static/docs/development/apps/api/testing.html +31 -8
  161. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +31 -8
  162. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +31 -8
  163. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +31 -8
  164. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +31 -8
  165. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +31 -8
  166. nautobot/project-static/docs/development/apps/api/views/base-template.html +31 -8
  167. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +31 -8
  168. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +31 -8
  169. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +31 -8
  170. nautobot/project-static/docs/development/apps/api/views/index.html +31 -8
  171. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +31 -8
  172. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -8
  173. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +31 -8
  174. nautobot/project-static/docs/development/apps/api/views/notes.html +31 -8
  175. nautobot/project-static/docs/development/apps/api/views/rest-api.html +31 -8
  176. nautobot/project-static/docs/development/apps/api/views/urls.html +31 -8
  177. nautobot/project-static/docs/development/apps/index.html +31 -8
  178. nautobot/project-static/docs/development/apps/migration/code-updates.html +31 -8
  179. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +31 -8
  180. nautobot/project-static/docs/development/apps/migration/from-v1.html +31 -8
  181. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +31 -8
  182. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +31 -8
  183. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +31 -8
  184. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +31 -8
  185. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +31 -8
  186. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +31 -8
  187. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +31 -8
  188. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +31 -8
  189. nautobot/project-static/docs/development/apps/porting-from-netbox.html +31 -8
  190. nautobot/project-static/docs/development/core/application-registry.html +31 -8
  191. nautobot/project-static/docs/development/core/best-practices.html +31 -8
  192. nautobot/project-static/docs/development/core/bootstrap-ui.html +31 -8
  193. nautobot/project-static/docs/development/core/caching.html +31 -8
  194. nautobot/project-static/docs/development/core/controllers.html +31 -8
  195. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +31 -8
  196. nautobot/project-static/docs/development/core/generic-views.html +31 -8
  197. nautobot/project-static/docs/development/core/getting-started.html +31 -8
  198. nautobot/project-static/docs/development/core/homepage.html +31 -8
  199. nautobot/project-static/docs/development/core/index.html +31 -8
  200. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +31 -8
  201. nautobot/project-static/docs/development/core/model-checklist.html +31 -8
  202. nautobot/project-static/docs/development/core/model-features.html +31 -8
  203. nautobot/project-static/docs/development/core/natural-keys.html +31 -8
  204. nautobot/project-static/docs/development/core/navigation-menu.html +31 -8
  205. nautobot/project-static/docs/development/core/release-checklist.html +31 -8
  206. nautobot/project-static/docs/development/core/role-internals.html +31 -8
  207. nautobot/project-static/docs/development/core/settings.html +31 -8
  208. nautobot/project-static/docs/development/core/style-guide.html +31 -8
  209. nautobot/project-static/docs/development/core/templates.html +31 -8
  210. nautobot/project-static/docs/development/core/testing.html +31 -8
  211. nautobot/project-static/docs/development/core/ui-component-framework.html +31 -8
  212. nautobot/project-static/docs/development/core/user-preferences.html +31 -8
  213. nautobot/project-static/docs/development/index.html +31 -8
  214. nautobot/project-static/docs/development/jobs/getting-started.html +35 -8
  215. nautobot/project-static/docs/development/jobs/index.html +31 -8
  216. nautobot/project-static/docs/development/jobs/installation.html +31 -8
  217. nautobot/project-static/docs/development/jobs/job-extensions.html +31 -8
  218. nautobot/project-static/docs/development/jobs/job-logging.html +31 -8
  219. nautobot/project-static/docs/development/jobs/job-patterns.html +31 -8
  220. nautobot/project-static/docs/development/jobs/job-structure.html +31 -8
  221. nautobot/project-static/docs/development/jobs/migration/from-v1.html +31 -8
  222. nautobot/project-static/docs/development/jobs/testing.html +31 -8
  223. nautobot/project-static/docs/index.html +31 -8
  224. nautobot/project-static/docs/insert-analytics.sh +36 -0
  225. nautobot/project-static/docs/objects.inv +0 -0
  226. nautobot/project-static/docs/overview/application_stack.html +31 -8
  227. nautobot/project-static/docs/overview/design_philosophy.html +31 -8
  228. nautobot/project-static/docs/release-notes/index.html +31 -8
  229. nautobot/project-static/docs/release-notes/version-1.0.html +31 -8
  230. nautobot/project-static/docs/release-notes/version-1.1.html +31 -8
  231. nautobot/project-static/docs/release-notes/version-1.2.html +31 -8
  232. nautobot/project-static/docs/release-notes/version-1.3.html +31 -8
  233. nautobot/project-static/docs/release-notes/version-1.4.html +31 -8
  234. nautobot/project-static/docs/release-notes/version-1.5.html +31 -8
  235. nautobot/project-static/docs/release-notes/version-1.6.html +328 -8
  236. nautobot/project-static/docs/release-notes/version-2.0.html +31 -8
  237. nautobot/project-static/docs/release-notes/version-2.1.html +31 -8
  238. nautobot/project-static/docs/release-notes/version-2.2.html +31 -8
  239. nautobot/project-static/docs/release-notes/version-2.3.html +31 -8
  240. nautobot/project-static/docs/release-notes/version-2.4.html +353 -8
  241. nautobot/project-static/docs/search/search_index.json +1 -1
  242. nautobot/project-static/docs/sitemap.xml +302 -298
  243. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  244. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +31 -8
  245. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +31 -8
  246. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +31 -8
  247. nautobot/project-static/docs/user-guide/administration/configuration/index.html +31 -8
  248. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +31 -8
  249. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +31 -8
  250. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +31 -8
  251. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +31 -8
  252. nautobot/project-static/docs/user-guide/administration/guides/docker.html +31 -8
  253. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +31 -8
  254. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +31 -8
  255. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +31 -8
  256. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +31 -8
  257. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +31 -8
  258. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +31 -8
  259. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +31 -8
  260. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +31 -8
  261. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +31 -8
  262. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +31 -8
  263. nautobot/project-static/docs/user-guide/administration/installation/index.html +31 -8
  264. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +31 -8
  265. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +31 -8
  266. nautobot/project-static/docs/user-guide/administration/installation/services.html +31 -8
  267. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +31 -8
  268. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +31 -8
  269. nautobot/project-static/docs/user-guide/administration/security/index.html +31 -9
  270. nautobot/project-static/docs/user-guide/administration/security/notices.html +144 -9
  271. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +31 -8
  272. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +31 -8
  273. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +31 -8
  274. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +31 -8
  275. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +31 -8
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +31 -8
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +31 -8
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +31 -8
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +31 -8
  280. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +31 -8
  281. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +31 -8
  282. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +31 -8
  283. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +31 -8
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +31 -8
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +31 -8
  286. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +31 -8
  287. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +31 -8
  288. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +31 -8
  289. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +31 -8
  290. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +31 -8
  291. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +31 -8
  292. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +31 -8
  293. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +31 -8
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +31 -8
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +31 -8
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +31 -8
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +31 -8
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +31 -8
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +31 -8
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +31 -8
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +31 -8
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +31 -8
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +31 -8
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +31 -8
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +43 -20
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +31 -8
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +31 -8
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +31 -8
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +31 -8
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +31 -8
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +31 -8
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +31 -8
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +31 -8
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +31 -8
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +31 -8
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +35 -8
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +35 -8
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +35 -8
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +10261 -0
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +34 -11
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +31 -8
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +31 -8
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +31 -8
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +31 -8
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +31 -8
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +31 -8
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +31 -8
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +31 -8
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +31 -8
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +31 -8
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +31 -8
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +31 -8
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +31 -8
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +31 -8
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +31 -8
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +31 -8
  337. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +31 -8
  338. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +31 -8
  339. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +31 -8
  340. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +31 -8
  341. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +31 -8
  342. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +31 -8
  343. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +31 -8
  344. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +31 -8
  345. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +31 -8
  346. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +31 -8
  347. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +31 -8
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +31 -8
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +31 -8
  350. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +31 -8
  351. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +31 -8
  352. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +31 -8
  353. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +31 -8
  354. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +31 -8
  355. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +31 -8
  356. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +31 -8
  357. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +31 -8
  358. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +31 -8
  359. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +31 -8
  360. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +31 -8
  361. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +31 -8
  362. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +31 -8
  363. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +31 -8
  364. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +31 -8
  365. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +31 -8
  366. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +31 -8
  367. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +31 -8
  368. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +31 -8
  369. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +31 -8
  370. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +31 -8
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +31 -8
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +31 -8
  373. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +41 -15
  374. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +31 -8
  375. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +31 -8
  376. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +31 -8
  377. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +31 -8
  378. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +31 -8
  379. nautobot/project-static/docs/user-guide/index.html +31 -8
  380. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +31 -8
  381. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +31 -8
  382. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +31 -8
  383. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +31 -8
  384. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +31 -8
  385. nautobot/project-static/docs/user-guide/platform-functionality/events.html +31 -8
  386. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +31 -8
  387. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +31 -8
  388. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +37 -9
  389. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +31 -8
  390. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +31 -8
  391. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +31 -8
  392. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +31 -8
  393. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +31 -8
  394. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +31 -8
  395. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +31 -8
  396. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +31 -8
  397. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +31 -8
  398. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +31 -8
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +31 -8
  400. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +31 -8
  401. nautobot/project-static/docs/user-guide/platform-functionality/note.html +31 -8
  402. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +31 -8
  403. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +31 -8
  404. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +31 -8
  405. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +31 -8
  406. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +31 -8
  407. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +31 -8
  408. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +31 -8
  409. nautobot/project-static/docs/user-guide/platform-functionality/role.html +31 -8
  410. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +31 -8
  411. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +31 -8
  412. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +31 -8
  413. nautobot/project-static/docs/user-guide/platform-functionality/status.html +31 -8
  414. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +31 -8
  415. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +31 -8
  416. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +31 -8
  417. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +31 -8
  418. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +31 -8
  419. nautobot/tenancy/tables.py +2 -0
  420. nautobot/users/models.py +4 -0
  421. nautobot/virtualization/models.py +4 -0
  422. nautobot/virtualization/tests/test_views.py +1 -1
  423. nautobot/wireless/forms.py +0 -1
  424. nautobot/wireless/models.py +1 -1
  425. nautobot/wireless/tables.py +7 -0
  426. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/METADATA +4 -4
  427. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/RECORD +433 -418
  428. /nautobot/dcim/templates/dcim/{platform_edit.html → platform_create.html} +0 -0
  429. /nautobot/extras/test_jobs/{pass.py → pass_job.py} +0 -0
  430. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/LICENSE.txt +0 -0
  431. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/NOTICE +0 -0
  432. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/WHEEL +0 -0
  433. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/entry_points.txt +0 -0
@@ -50,6 +50,7 @@ from nautobot.dcim.models import (
50
50
  Module,
51
51
  ModuleBay,
52
52
  ModuleBayTemplate,
53
+ ModuleFamily,
53
54
  ModuleType,
54
55
  Platform,
55
56
  PowerOutlet,
@@ -3404,6 +3405,130 @@ class ModuleTestCase(ModelTestCases.BaseModelTestCase):
3404
3405
  )
3405
3406
  self.assertEqual(child_module.interfaces.first().name, "Interface " + module_bay_position + "/3/2")
3406
3407
 
3408
+ def test_module_manufacturer_constraint_requires_first_party_false(self):
3409
+ """Test that modules are allowed when requires_first_party_modules is False, regardless of manufacturer."""
3410
+ manufacturer = Manufacturer.objects.create(name="Different Manufacturer")
3411
+ module_type = ModuleType.objects.create(manufacturer=manufacturer, model="Different Module")
3412
+ module_bay = ModuleBay.objects.create(
3413
+ parent_device=self.device, position="test1", requires_first_party_modules=False
3414
+ )
3415
+ module = Module(
3416
+ module_type=module_type,
3417
+ parent_module_bay=module_bay,
3418
+ status=self.status,
3419
+ )
3420
+
3421
+ module.full_clean()
3422
+ module.save()
3423
+
3424
+ def test_module_manufacturer_constraint_device_parent_same_manufacturer(self):
3425
+ """Test that modules are allowed when requires_first_party_modules is True and manufacturers match."""
3426
+ device_manufacturer = self.device.device_type.manufacturer
3427
+ module_type = ModuleType.objects.create(manufacturer=device_manufacturer, model="Same Manufacturer Module")
3428
+ module_bay = ModuleBay.objects.create(
3429
+ parent_device=self.device, position="test2", requires_first_party_modules=True
3430
+ )
3431
+ module = Module(
3432
+ module_type=module_type,
3433
+ parent_module_bay=module_bay,
3434
+ status=self.status,
3435
+ )
3436
+
3437
+ module.full_clean()
3438
+ module.save()
3439
+
3440
+ def test_module_manufacturer_constraint_device_parent_different_manufacturer(self):
3441
+ """Test that modules are rejected when requires_first_party_modules is True and manufacturers don't match."""
3442
+ manufacturer = Manufacturer.objects.create(name="Different Manufacturer")
3443
+ module_type = ModuleType.objects.create(manufacturer=manufacturer, model="Different Module")
3444
+ module_bay = ModuleBay.objects.create(
3445
+ parent_device=self.device, position="test3", requires_first_party_modules=True
3446
+ )
3447
+ module = Module(
3448
+ module_type=module_type,
3449
+ parent_module_bay=module_bay,
3450
+ status=self.status,
3451
+ )
3452
+
3453
+ with self.assertRaises(ValidationError) as context:
3454
+ module.full_clean()
3455
+
3456
+ self.assertIn("module_type", context.exception.message_dict)
3457
+ self.assertIn(
3458
+ "The selected module bay requires a module type from the same manufacturer as the parent device or module",
3459
+ context.exception.message_dict["module_type"],
3460
+ )
3461
+
3462
+ def test_module_manufacturer_constraint_module_parent_same_manufacturer(self):
3463
+ """Test that modules are allowed when requires_first_party_modules is True and manufacturers match."""
3464
+ manufacturer = Manufacturer.objects.create(name="Parent Manufacturer")
3465
+ parent_module_type = ModuleType.objects.create(manufacturer=manufacturer, model="Parent Module")
3466
+ ModuleBayTemplate.objects.create(module_type=parent_module_type, position="child1")
3467
+
3468
+ parent_module = Module.objects.create(
3469
+ module_type=parent_module_type,
3470
+ location=self.location,
3471
+ status=self.status,
3472
+ )
3473
+ child_module_type = ModuleType.objects.create(manufacturer=manufacturer, model="Child Module")
3474
+ parent_module_bay = parent_module.module_bays.first()
3475
+ parent_module_bay.requires_first_party_modules = True
3476
+ parent_module_bay.save()
3477
+
3478
+ child_module = Module(
3479
+ module_type=child_module_type,
3480
+ parent_module_bay=parent_module_bay,
3481
+ status=self.status,
3482
+ )
3483
+
3484
+ child_module.full_clean()
3485
+ child_module.save()
3486
+
3487
+ def test_module_manufacturer_constraint_module_parent_different_manufacturer(self):
3488
+ """Test that modules are rejected when requires_first_party_modules is True and manufacturers don't match."""
3489
+ parent_manufacturer = Manufacturer.objects.create(name="Parent Manufacturer")
3490
+ parent_module_type = ModuleType.objects.create(manufacturer=parent_manufacturer, model="Parent Module")
3491
+ ModuleBayTemplate.objects.create(module_type=parent_module_type, position="child2")
3492
+
3493
+ parent_module = Module.objects.create(
3494
+ module_type=parent_module_type,
3495
+ location=self.location,
3496
+ status=self.status,
3497
+ )
3498
+ child_manufacturer = Manufacturer.objects.create(name="Child Manufacturer")
3499
+ child_module_type = ModuleType.objects.create(manufacturer=child_manufacturer, model="Child Module")
3500
+ parent_module_bay = parent_module.module_bays.first()
3501
+ parent_module_bay.requires_first_party_modules = True
3502
+ parent_module_bay.save()
3503
+
3504
+ child_module = Module(
3505
+ module_type=child_module_type,
3506
+ parent_module_bay=parent_module_bay,
3507
+ status=self.status,
3508
+ )
3509
+
3510
+ with self.assertRaises(ValidationError) as context:
3511
+ child_module.full_clean()
3512
+
3513
+ self.assertIn("module_type", context.exception.message_dict)
3514
+ self.assertIn(
3515
+ "The selected module bay requires a module type from the same manufacturer as the parent device or module",
3516
+ context.exception.message_dict["module_type"],
3517
+ )
3518
+
3519
+ def test_module_manufacturer_constraint_no_parent_module_bay(self):
3520
+ """Test that manufacturer constraint validation is skipped when parent_module_bay is None."""
3521
+ manufacturer = Manufacturer.objects.create(name="Any Manufacturer")
3522
+ module_type = ModuleType.objects.create(manufacturer=manufacturer, model="Any Module")
3523
+ module = Module(
3524
+ module_type=module_type,
3525
+ location=self.location,
3526
+ status=self.status,
3527
+ )
3528
+
3529
+ module.full_clean()
3530
+ module.save()
3531
+
3407
3532
 
3408
3533
  class ModuleTypeTestCase(ModelTestCases.BaseModelTestCase):
3409
3534
  model = ModuleType
@@ -3507,3 +3632,24 @@ class VirtualDeviceContextTestCase(ModelTestCases.BaseModelTestCase):
3507
3632
  vdc.validated_save()
3508
3633
 
3509
3634
  self.assertIn("Virtual Device Context's device cannot be changed once created", str(err.exception))
3635
+
3636
+
3637
+ class ModuleFamilyTestCase(ModelTestCases.BaseModelTestCase):
3638
+ """Test cases for the ModuleFamily model."""
3639
+
3640
+ model = ModuleFamily
3641
+
3642
+ def setUp(self):
3643
+ """Create a ModuleFamily for use in test methods."""
3644
+ self.module_family = ModuleFamily.objects.create(
3645
+ name="Test Module Family", description="A module family for testing"
3646
+ )
3647
+
3648
+ def test_create_modulefamily(self):
3649
+ """Test the creation of a ModuleFamily instance."""
3650
+ self.assertEqual(self.module_family.name, "Test Module Family")
3651
+ self.assertEqual(self.module_family.description, "A module family for testing")
3652
+
3653
+ def test_modulefamily_str(self):
3654
+ """Test string representation of ModuleFamily."""
3655
+ self.assertEqual(str(self.module_family), "Test Module Family")
@@ -85,6 +85,7 @@ from nautobot.dcim.models import (
85
85
  Module,
86
86
  ModuleBay,
87
87
  ModuleBayTemplate,
88
+ ModuleFamily,
88
89
  ModuleType,
89
90
  Platform,
90
91
  PowerFeed,
@@ -2007,6 +2008,7 @@ class ModuleBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCas
2007
2008
  def setUpTestData(cls):
2008
2009
  device_type = DeviceType.objects.first()
2009
2010
  module_type = ModuleType.objects.first()
2011
+ module_family = ModuleFamily.objects.create(name="Test Module Family")
2010
2012
 
2011
2013
  cls.form_data = {
2012
2014
  "device_type": device_type.pk,
@@ -2015,6 +2017,7 @@ class ModuleBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCas
2015
2017
  "position": "Test modulebaytemplate position",
2016
2018
  "description": "Test modulebaytemplate description",
2017
2019
  "label": "Test modulebaytemplate label",
2020
+ "module_family": module_family.pk,
2018
2021
  }
2019
2022
 
2020
2023
  cls.bulk_create_data = {
@@ -2023,10 +2026,12 @@ class ModuleBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCas
2023
2026
  "position_pattern": "Test Module Bay Template Position [10-12]",
2024
2027
  "label_pattern": "Test modulebaytemplate label [1-3]",
2025
2028
  "description": "Test modulebaytemplate description",
2029
+ "module_family": module_family.pk,
2026
2030
  }
2027
2031
 
2028
2032
  cls.bulk_edit_data = {
2029
2033
  "description": "Description changed",
2034
+ "module_family": module_family.pk,
2030
2035
  }
2031
2036
 
2032
2037
  test_instance = cls.model.objects.first()
@@ -2037,6 +2042,7 @@ class ModuleBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCas
2037
2042
  "position": "new test position",
2038
2043
  "label": "new test label",
2039
2044
  "description": "new test description",
2045
+ "module_family": module_family.pk,
2040
2046
  }
2041
2047
 
2042
2048
 
@@ -2560,6 +2566,16 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2560
2566
 
2561
2567
  class ModuleTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2562
2568
  model = Module
2569
+ custom_action_required_permissions = {
2570
+ "dcim:module_consoleports": ["dcim.view_module", "dcim.view_consoleport"],
2571
+ "dcim:module_consoleserverports": ["dcim.view_module", "dcim.view_consoleserverport"],
2572
+ "dcim:module_powerports": ["dcim.view_module", "dcim.view_powerport"],
2573
+ "dcim:module_poweroutlets": ["dcim.view_module", "dcim.view_poweroutlet"],
2574
+ "dcim:module_interfaces": ["dcim.view_module", "dcim.view_interface"],
2575
+ "dcim:module_rearports": ["dcim.view_module", "dcim.view_rearport"],
2576
+ "dcim:module_frontports": ["dcim.view_module", "dcim.view_frontport"],
2577
+ "dcim:module_modulebays": ["dcim.view_module", "dcim.view_modulebay"],
2578
+ }
2563
2579
 
2564
2580
  @classmethod
2565
2581
  def setUpTestData(cls):
@@ -2668,50 +2684,6 @@ class ModuleTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2668
2684
  "status": statuses[2].pk,
2669
2685
  }
2670
2686
 
2671
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2672
- def test_module_consoleports(self):
2673
- module = Module.objects.first()
2674
-
2675
- ConsolePort.objects.create(module=module, name="Console Port 1")
2676
- ConsolePort.objects.create(module=module, name="Console Port 2")
2677
- ConsolePort.objects.create(module=module, name="Console Port 3")
2678
-
2679
- url = reverse("dcim:module_consoleports", kwargs={"pk": module.pk})
2680
- self.assertHttpStatus(self.client.get(url), 200)
2681
-
2682
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2683
- def test_module_consoleserverports(self):
2684
- module = Module.objects.first()
2685
-
2686
- ConsoleServerPort.objects.create(module=module, name="Console Server Port 1")
2687
- ConsoleServerPort.objects.create(module=module, name="Console Server Port 2")
2688
- ConsoleServerPort.objects.create(module=module, name="Console Server Port 3")
2689
-
2690
- url = reverse("dcim:module_consoleserverports", kwargs={"pk": module.pk})
2691
- self.assertHttpStatus(self.client.get(url), 200)
2692
-
2693
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2694
- def test_module_powerports(self):
2695
- module = Module.objects.first()
2696
-
2697
- PowerPort.objects.create(module=module, name="Power Port 1")
2698
- PowerPort.objects.create(module=module, name="Power Port 2")
2699
- PowerPort.objects.create(module=module, name="Power Port 3")
2700
-
2701
- url = reverse("dcim:module_powerports", kwargs={"pk": module.pk})
2702
- self.assertHttpStatus(self.client.get(url), 200)
2703
-
2704
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2705
- def test_module_poweroutlets(self):
2706
- module = Module.objects.first()
2707
-
2708
- PowerOutlet.objects.create(module=module, name="Power Outlet 1")
2709
- PowerOutlet.objects.create(module=module, name="Power Outlet 2")
2710
- PowerOutlet.objects.create(module=module, name="Power Outlet 3")
2711
-
2712
- url = reverse("dcim:module_poweroutlets", kwargs={"pk": module.pk})
2713
- self.assertHttpStatus(self.client.get(url), 200)
2714
-
2715
2687
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2716
2688
  def test_module_interfaces(self):
2717
2689
  module = Module.objects.filter(interfaces__isnull=False).first()
@@ -2793,59 +2765,6 @@ class ModuleTestCase(ViewTestCases.PrimaryObjectViewTestCase):
2793
2765
  sorted(interface_ips),
2794
2766
  )
2795
2767
 
2796
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2797
- def test_module_rearports(self):
2798
- module = Module.objects.first()
2799
-
2800
- RearPort.objects.create(module=module, name="Rear Port 1")
2801
- RearPort.objects.create(module=module, name="Rear Port 2")
2802
- RearPort.objects.create(module=module, name="Rear Port 3")
2803
-
2804
- url = reverse("dcim:module_rearports", kwargs={"pk": module.pk})
2805
- self.assertHttpStatus(self.client.get(url), 200)
2806
-
2807
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2808
- def test_module_frontports(self):
2809
- module = Module.objects.first()
2810
- rear_ports = (
2811
- RearPort.objects.create(module=module, name="Rear Port 1"),
2812
- RearPort.objects.create(module=module, name="Rear Port 2"),
2813
- RearPort.objects.create(module=module, name="Rear Port 3"),
2814
- )
2815
-
2816
- FrontPort.objects.create(
2817
- module=module,
2818
- name="Front Port 1",
2819
- rear_port=rear_ports[0],
2820
- rear_port_position=1,
2821
- )
2822
- FrontPort.objects.create(
2823
- module=module,
2824
- name="Front Port 2",
2825
- rear_port=rear_ports[1],
2826
- rear_port_position=1,
2827
- )
2828
- FrontPort.objects.create(
2829
- module=module,
2830
- name="Front Port 3",
2831
- rear_port=rear_ports[2],
2832
- rear_port_position=1,
2833
- )
2834
-
2835
- url = reverse("dcim:module_frontports", kwargs={"pk": module.pk})
2836
- self.assertHttpStatus(self.client.get(url), 200)
2837
-
2838
- @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
2839
- def test_module_modulebays(self):
2840
- module = Module.objects.first()
2841
-
2842
- ModuleBay.objects.create(parent_module=module, name="Test View Module Bay 1")
2843
- ModuleBay.objects.create(parent_module=module, name="Test View Module Bay 2")
2844
- ModuleBay.objects.create(parent_module=module, name="Test View Module Bay 3")
2845
-
2846
- url = reverse("dcim:module_modulebays", kwargs={"pk": module.pk})
2847
- self.assertHttpStatus(self.client.get(url), 200)
2848
-
2849
2768
 
2850
2769
  class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
2851
2770
  model = ConsolePort
@@ -4587,6 +4506,15 @@ class InterfaceRedundancyGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4587
4506
  class SoftwareImageFileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4588
4507
  model = SoftwareImageFile
4589
4508
  filterset = SoftwareImageFileFilterSet
4509
+ custom_action_required_permissions = {
4510
+ "dcim:softwareimagefile_devices": ["dcim.view_softwareimagefile", "dcim.view_device"],
4511
+ "dcim:softwareimagefile_device_types": ["dcim.view_softwareimagefile", "dcim.view_devicetype"],
4512
+ "dcim:softwareimagefile_virtual_machines": [
4513
+ "dcim.view_softwareimagefile",
4514
+ "virtualization.view_virtualmachine",
4515
+ ],
4516
+ "dcim:softwareimagefile_inventory_items": ["dcim.view_softwareimagefile", "dcim.view_inventoryitem"],
4517
+ }
4590
4518
 
4591
4519
  @classmethod
4592
4520
  def setUpTestData(cls):
@@ -4659,6 +4587,12 @@ class SoftwareVersionTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4659
4587
  class ControllerTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4660
4588
  model = Controller
4661
4589
  filterset = ControllerFilterSet
4590
+ custom_action_required_permissions = {
4591
+ "dcim:controller_wirelessnetworks": [
4592
+ "dcim.view_controller",
4593
+ "wireless.view_controllermanageddevicegroupwirelessnetworkassignment",
4594
+ ],
4595
+ }
4662
4596
 
4663
4597
  @classmethod
4664
4598
  def setUpTestData(cls):
@@ -4695,6 +4629,16 @@ class ControllerTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4695
4629
  class ControllerManagedDeviceGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4696
4630
  model = ControllerManagedDeviceGroup
4697
4631
  filterset = ControllerManagedDeviceGroupFilterSet
4632
+ custom_action_required_permissions = {
4633
+ "dcim:controllermanageddevicegroup_wireless_networks": [
4634
+ "dcim.view_controllermanageddevicegroup",
4635
+ "wireless.view_controllermanageddevicegroupwirelessnetworkassignment",
4636
+ ],
4637
+ "dcim:controllermanageddevicegroup_radio_profiles": [
4638
+ "dcim.view_controllermanageddevicegroup",
4639
+ "wireless.view_radioprofile",
4640
+ ],
4641
+ }
4698
4642
 
4699
4643
  @classmethod
4700
4644
  def setUpTestData(cls):
@@ -4781,3 +4725,32 @@ class VirtualDeviceContextTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4781
4725
  vdc.refresh_from_db()
4782
4726
  self.assertEqual(vdc.primary_ip6, ip_v6)
4783
4727
  self.assertEqual(vdc.primary_ip4, ip_v4)
4728
+
4729
+
4730
+ class ModuleFamilyTestCase(ViewTestCases.PrimaryObjectViewTestCase):
4731
+ """Test cases for ModuleFamily views."""
4732
+
4733
+ model = ModuleFamily
4734
+
4735
+ @classmethod
4736
+ def setUpTestData(cls):
4737
+ """Create test data for ModuleFamily views."""
4738
+ ModuleFamily.objects.create(name="Module Family 1", description="First Module Family")
4739
+ ModuleFamily.objects.create(name="Module Family 2", description="Second Module Family")
4740
+ ModuleFamily.objects.create(name="Module Family 3", description="Third Module Family")
4741
+
4742
+ cls.form_data = {
4743
+ "name": "Module Family X",
4744
+ "description": "A new module family",
4745
+ }
4746
+
4747
+ cls.csv_data = (
4748
+ "name,description",
4749
+ "Module Family 4,Fourth Module Family",
4750
+ "Module Family 5,Fifth Module Family",
4751
+ "Module Family 6,Sixth Module Family",
4752
+ )
4753
+
4754
+ cls.bulk_edit_data = {
4755
+ "description": "Modified description",
4756
+ }
nautobot/dcim/urls.py CHANGED
@@ -34,12 +34,14 @@ router.register("device-redundancy-groups", views.DeviceRedundancyGroupUIViewSet
34
34
  router.register("device-types", views.DeviceTypeUIViewSet)
35
35
  router.register("interface-redundancy-groups", views.InterfaceRedundancyGroupUIViewSet)
36
36
  router.register("interface-redundancy-groups-associations", views.InterfaceRedundancyGroupAssociationUIViewSet)
37
+ router.register("locations", views.LocationUIViewSet)
37
38
  router.register("location-types", views.LocationTypeUIViewSet)
38
39
  router.register("manufacturers", views.ManufacturerUIViewSet)
39
- router.register("module-bays", views.ModuleBayUIViewSet)
40
40
  router.register("module-bay-templates", views.ModuleBayTemplateUIViewSet)
41
- router.register("modules", views.ModuleUIViewSet)
41
+ router.register("module-bays", views.ModuleBayUIViewSet)
42
+ router.register("module-families", views.ModuleFamilyUIViewSet)
42
43
  router.register("module-types", views.ModuleTypeUIViewSet)
44
+ router.register("modules", views.ModuleUIViewSet)
43
45
  router.register("platforms", views.PlatformUIViewSet)
44
46
  router.register("power-feeds", views.PowerFeedUIViewSet)
45
47
  router.register("power-panels", views.PowerPanelUIViewSet)
@@ -51,26 +53,6 @@ router.register("virtual-device-contexts", views.VirtualDeviceContextUIViewSet)
51
53
 
52
54
  urlpatterns = [
53
55
  # Locations
54
- path("locations/", views.LocationListView.as_view(), name="location_list"),
55
- path("locations/add/", views.LocationEditView.as_view(), name="location_add"),
56
- path("locations/edit/", views.LocationBulkEditView.as_view(), name="location_bulk_edit"),
57
- path("locations/import/", views.LocationBulkImportView.as_view(), name="location_import"), # 3.0 TODO: remove
58
- path("locations/delete/", views.LocationBulkDeleteView.as_view(), name="location_bulk_delete"),
59
- path("locations/<uuid:pk>/", views.LocationView.as_view(), name="location"),
60
- path("locations/<uuid:pk>/edit/", views.LocationEditView.as_view(), name="location_edit"),
61
- path("locations/<uuid:pk>/delete/", views.LocationDeleteView.as_view(), name="location_delete"),
62
- path(
63
- "locations/<uuid:pk>/changelog/",
64
- ObjectChangeLogView.as_view(),
65
- name="location_changelog",
66
- kwargs={"model": Location},
67
- ),
68
- path(
69
- "locations/<uuid:pk>/notes/",
70
- ObjectNotesView.as_view(),
71
- name="location_notes",
72
- kwargs={"model": Location},
73
- ),
74
56
  path(
75
57
  "locations/<uuid:pk>/migrate-data-to-contact/",
76
58
  views.MigrateLocationDataToContactView.as_view(),