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
nautobot/dcim/forms.py CHANGED
@@ -133,6 +133,7 @@ from .models import (
133
133
  Module,
134
134
  ModuleBay,
135
135
  ModuleBayTemplate,
136
+ ModuleFamily,
136
137
  ModuleType,
137
138
  Platform,
138
139
  PowerFeed,
@@ -622,7 +623,6 @@ class RackBulkEditForm(
622
623
  class Meta:
623
624
  model = Rack
624
625
  nullable_fields = [
625
- "location",
626
626
  "rack_group",
627
627
  "tenant",
628
628
  "serial",
@@ -864,6 +864,39 @@ class DeviceFamilyBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
864
864
  nullable_fields = []
865
865
 
866
866
 
867
+ class ModuleFamilyForm(NautobotModelForm):
868
+ """ModuleFamily creation/edit form."""
869
+
870
+ class Meta:
871
+ model = ModuleFamily
872
+ fields = [
873
+ "name",
874
+ "description",
875
+ "tags",
876
+ ]
877
+
878
+
879
+ class ModuleFamilyFilterForm(NautobotFilterForm):
880
+ """ModuleFamily filter form."""
881
+
882
+ model = ModuleFamily
883
+ q = forms.CharField(required=False, label="Search")
884
+ module_types = DynamicModelMultipleChoiceField(
885
+ queryset=ModuleType.objects.all(), to_field_name="model", required=False
886
+ )
887
+ tags = TagFilterField(model)
888
+
889
+
890
+ class ModuleFamilyBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
891
+ """ModuleFamily bulk edit form."""
892
+
893
+ pk = forms.ModelMultipleChoiceField(queryset=ModuleFamily.objects.all(), widget=forms.MultipleHiddenInput())
894
+ description = forms.CharField(required=False)
895
+
896
+ class Meta:
897
+ nullable_fields = []
898
+
899
+
867
900
  #
868
901
  # Device types
869
902
  #
@@ -997,6 +1030,11 @@ class DeviceTypeFilterForm(NautobotFilterForm):
997
1030
 
998
1031
  class ModuleTypeForm(NautobotModelForm):
999
1032
  manufacturer = DynamicModelChoiceField(queryset=Manufacturer.objects.all())
1033
+ module_family = DynamicModelChoiceField(
1034
+ queryset=ModuleFamily.objects.all(),
1035
+ required=False,
1036
+ label="Family",
1037
+ )
1000
1038
  comments = CommentField(label="Comments")
1001
1039
 
1002
1040
  class Meta:
@@ -1004,6 +1042,7 @@ class ModuleTypeForm(NautobotModelForm):
1004
1042
  fields = [
1005
1043
  "manufacturer",
1006
1044
  "model",
1045
+ "module_family",
1007
1046
  "part_number",
1008
1047
  "comments",
1009
1048
  "tags",
@@ -1034,11 +1073,12 @@ class ModuleTypeImportForm(BootstrapMixin, forms.ModelForm):
1034
1073
  class ModuleTypeBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
1035
1074
  pk = forms.ModelMultipleChoiceField(queryset=ModuleType.objects.all(), widget=forms.MultipleHiddenInput())
1036
1075
  manufacturer = DynamicModelChoiceField(queryset=Manufacturer.objects.all(), required=False)
1076
+ module_family = DynamicModelChoiceField(queryset=ModuleFamily.objects.all(), required=False, label="Family")
1037
1077
  part_number = forms.CharField(required=False)
1038
1078
  comments = CommentField(label="Comments", required=False)
1039
1079
 
1040
1080
  class Meta:
1041
- nullable_fields = []
1081
+ nullable_fields = ["module_family"]
1042
1082
 
1043
1083
 
1044
1084
  class ModuleTypeFilterForm(NautobotFilterForm):
@@ -1047,6 +1087,9 @@ class ModuleTypeFilterForm(NautobotFilterForm):
1047
1087
  manufacturer = DynamicModelMultipleChoiceField(
1048
1088
  queryset=Manufacturer.objects.all(), to_field_name="name", required=False
1049
1089
  )
1090
+ module_family = DynamicModelMultipleChoiceField(
1091
+ queryset=ModuleFamily.objects.all(), to_field_name="name", required=False
1092
+ )
1050
1093
  has_console_ports = forms.NullBooleanField(
1051
1094
  required=False,
1052
1095
  label="Has console ports",
@@ -1093,9 +1136,15 @@ class ModularComponentTemplateForm(ComponentTemplateForm):
1093
1136
  queryset=DeviceType.objects.all(),
1094
1137
  required=False,
1095
1138
  )
1139
+ module_family = DynamicModelChoiceField(
1140
+ queryset=ModuleFamily.objects.all(),
1141
+ required=False,
1142
+ label="Family",
1143
+ )
1096
1144
  module_type = DynamicModelChoiceField(
1097
1145
  queryset=ModuleType.objects.all(),
1098
1146
  required=False,
1147
+ query_params={"module_family": "$module_family"},
1099
1148
  )
1100
1149
 
1101
1150
 
@@ -1135,9 +1184,15 @@ class ModularComponentTemplateCreateForm(ComponentTemplateCreateForm):
1135
1184
  queryset=DeviceType.objects.all(),
1136
1185
  required=False,
1137
1186
  )
1187
+ module_family = DynamicModelChoiceField(
1188
+ queryset=ModuleFamily.objects.all(),
1189
+ required=False,
1190
+ label="Family",
1191
+ )
1138
1192
  module_type = DynamicModelChoiceField(
1139
1193
  queryset=ModuleType.objects.all(),
1140
1194
  required=False,
1195
+ query_params={"module_family": "$module_family"},
1141
1196
  )
1142
1197
 
1143
1198
 
@@ -1146,6 +1201,7 @@ class ConsolePortTemplateForm(ModularComponentTemplateForm):
1146
1201
  model = ConsolePortTemplate
1147
1202
  fields = [
1148
1203
  "device_type",
1204
+ "module_family",
1149
1205
  "module_type",
1150
1206
  "name",
1151
1207
  "label",
@@ -1158,6 +1214,7 @@ class ConsolePortTemplateCreateForm(ModularComponentTemplateCreateForm):
1158
1214
  type = forms.ChoiceField(choices=add_blank_choice(ConsolePortTypeChoices), widget=StaticSelect2())
1159
1215
  field_order = (
1160
1216
  "device_type",
1217
+ "module_family",
1161
1218
  "module_type",
1162
1219
  "name_pattern",
1163
1220
  "label_pattern",
@@ -1184,6 +1241,7 @@ class ConsoleServerPortTemplateForm(ModularComponentTemplateForm):
1184
1241
  model = ConsoleServerPortTemplate
1185
1242
  fields = [
1186
1243
  "device_type",
1244
+ "module_family",
1187
1245
  "module_type",
1188
1246
  "name",
1189
1247
  "label",
@@ -1196,6 +1254,7 @@ class ConsoleServerPortTemplateCreateForm(ModularComponentTemplateCreateForm):
1196
1254
  type = forms.ChoiceField(choices=add_blank_choice(ConsolePortTypeChoices), widget=StaticSelect2())
1197
1255
  field_order = (
1198
1256
  "device_type",
1257
+ "module_family",
1199
1258
  "module_type",
1200
1259
  "name_pattern",
1201
1260
  "label_pattern",
@@ -1226,6 +1285,7 @@ class PowerPortTemplateForm(ModularComponentTemplateForm):
1226
1285
  model = PowerPortTemplate
1227
1286
  fields = [
1228
1287
  "device_type",
1288
+ "module_family",
1229
1289
  "module_type",
1230
1290
  "name",
1231
1291
  "label",
@@ -1242,6 +1302,7 @@ class PowerPortTemplateCreateForm(ModularComponentTemplateCreateForm):
1242
1302
  allocated_draw = forms.IntegerField(min_value=1, required=False, help_text="Allocated power draw (watts)")
1243
1303
  field_order = (
1244
1304
  "device_type",
1305
+ "module_family",
1245
1306
  "module_type",
1246
1307
  "name_pattern",
1247
1308
  "label_pattern",
@@ -1279,6 +1340,7 @@ class PowerOutletTemplateForm(ModularComponentTemplateForm):
1279
1340
  model = PowerOutletTemplate
1280
1341
  fields = [
1281
1342
  "device_type",
1343
+ "module_family",
1282
1344
  "module_type",
1283
1345
  "name",
1284
1346
  "label",
@@ -1316,6 +1378,7 @@ class PowerOutletTemplateCreateForm(ModularComponentTemplateCreateForm):
1316
1378
  )
1317
1379
  field_order = (
1318
1380
  "device_type",
1381
+ "module_family",
1319
1382
  "module_type",
1320
1383
  "name_pattern",
1321
1384
  "label_pattern",
@@ -1367,6 +1430,7 @@ class InterfaceTemplateForm(ModularComponentTemplateForm):
1367
1430
  model = InterfaceTemplate
1368
1431
  fields = [
1369
1432
  "device_type",
1433
+ "module_family",
1370
1434
  "module_type",
1371
1435
  "name",
1372
1436
  "label",
@@ -1384,6 +1448,7 @@ class InterfaceTemplateCreateForm(ModularComponentTemplateCreateForm):
1384
1448
  mgmt_only = forms.BooleanField(required=False, label="Management only")
1385
1449
  field_order = (
1386
1450
  "device_type",
1451
+ "module_family",
1387
1452
  "module_type",
1388
1453
  "name_pattern",
1389
1454
  "label_pattern",
@@ -1413,6 +1478,7 @@ class FrontPortTemplateForm(ModularComponentTemplateForm):
1413
1478
  model = FrontPortTemplate
1414
1479
  fields = [
1415
1480
  "device_type",
1481
+ "module_family",
1416
1482
  "module_type",
1417
1483
  "name",
1418
1484
  "label",
@@ -1448,6 +1514,7 @@ class FrontPortTemplateCreateForm(ModularComponentTemplateCreateForm):
1448
1514
  )
1449
1515
  field_order = (
1450
1516
  "device_type",
1517
+ "module_family",
1451
1518
  "module_type",
1452
1519
  "name_pattern",
1453
1520
  "label_pattern",
@@ -1534,6 +1601,7 @@ class RearPortTemplateForm(ModularComponentTemplateForm):
1534
1601
  model = RearPortTemplate
1535
1602
  fields = [
1536
1603
  "device_type",
1604
+ "module_family",
1537
1605
  "module_type",
1538
1606
  "name",
1539
1607
  "label",
@@ -1559,6 +1627,7 @@ class RearPortTemplateCreateForm(ModularComponentTemplateCreateForm):
1559
1627
  )
1560
1628
  field_order = (
1561
1629
  "device_type",
1630
+ "module_family",
1562
1631
  "module_type",
1563
1632
  "name_pattern",
1564
1633
  "label_pattern",
@@ -1616,6 +1685,7 @@ class ModuleBayTemplateForm(ModularComponentTemplateForm):
1616
1685
  model = ModuleBayTemplate
1617
1686
  fields = [
1618
1687
  "device_type",
1688
+ "module_family",
1619
1689
  "module_type",
1620
1690
  "name",
1621
1691
  "position",
@@ -1623,8 +1693,20 @@ class ModuleBayTemplateForm(ModularComponentTemplateForm):
1623
1693
  "description",
1624
1694
  ]
1625
1695
 
1696
+ def __init__(self, *args, **kwargs):
1697
+ super().__init__(*args, **kwargs)
1698
+ self.fields[
1699
+ "module_family"
1700
+ ].help_text = "If assigned to a family, this module bay will only accept module types in the same family."
1701
+
1626
1702
 
1627
1703
  class ModuleBayBaseCreateForm(BootstrapMixin, forms.Form):
1704
+ module_family = DynamicModelChoiceField(
1705
+ queryset=ModuleFamily.objects.all(),
1706
+ required=False,
1707
+ label="Family",
1708
+ help_text="This bay will only accept module types assigned to this family",
1709
+ )
1628
1710
  name_pattern = ExpandableNameField(label="Name")
1629
1711
  label_pattern = ExpandableNameField(
1630
1712
  label="Label",
@@ -1636,6 +1718,11 @@ class ModuleBayBaseCreateForm(BootstrapMixin, forms.Form):
1636
1718
  help_text="Alphanumeric ranges are supported. (Must match the number of names being created.)"
1637
1719
  " Default to the names of the module bays unless manually supplied by the user.",
1638
1720
  )
1721
+ requires_first_party_modules = forms.BooleanField(
1722
+ required=False,
1723
+ label="Requires first-party modules",
1724
+ help_text="This bay will only accept module types from the same manufacturer as the parent device or module",
1725
+ )
1639
1726
  description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
1640
1727
 
1641
1728
  def clean(self):
@@ -1675,14 +1762,17 @@ class ModuleBayTemplateCreateForm(ModuleBayBaseCreateForm):
1675
1762
  module_type = DynamicModelChoiceField(
1676
1763
  queryset=ModuleType.objects.all(),
1677
1764
  required=False,
1765
+ query_params={"module_family": "$module_family"},
1678
1766
  )
1679
1767
 
1680
1768
  field_order = (
1681
1769
  "device_type",
1770
+ "module_family",
1682
1771
  "module_type",
1683
1772
  "name_pattern",
1684
1773
  "label_pattern",
1685
1774
  "position_pattern",
1775
+ "requires_first_party_modules",
1686
1776
  "description",
1687
1777
  )
1688
1778
 
@@ -1692,9 +1782,15 @@ class ModuleBayTemplateBulkEditForm(NautobotBulkEditForm):
1692
1782
  label = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
1693
1783
  description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
1694
1784
  position = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
1785
+ module_family = DynamicModelChoiceField(
1786
+ queryset=ModuleFamily.objects.all(),
1787
+ required=False,
1788
+ label="Family",
1789
+ help_text="Module family that can be installed in this bay",
1790
+ )
1695
1791
 
1696
1792
  class Meta:
1697
- nullable_fields = ("label", "description")
1793
+ nullable_fields = ("label", "description", "module_family")
1698
1794
 
1699
1795
 
1700
1796
  #
@@ -2202,14 +2298,12 @@ class DeviceBulkEditForm(
2202
2298
  class Meta:
2203
2299
  model = Device
2204
2300
  nullable_fields = [
2205
- "location",
2206
2301
  "tenant",
2207
2302
  "platform",
2208
2303
  "serial",
2209
2304
  "rack",
2210
2305
  "position",
2211
2306
  "face",
2212
- "rack_group",
2213
2307
  "cluster",
2214
2308
  "comments",
2215
2309
  "secrets_group",
@@ -2370,9 +2464,18 @@ class ModuleForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
2370
2464
  required=False,
2371
2465
  initial_params={"module_types": "$module_type"},
2372
2466
  )
2467
+ module_family = DynamicModelChoiceField(
2468
+ queryset=ModuleFamily.objects.all(),
2469
+ required=False,
2470
+ label="Family",
2471
+ )
2373
2472
  module_type = DynamicModelChoiceField(
2374
2473
  queryset=ModuleType.objects.all(),
2375
- query_params={"manufacturer": "$manufacturer"},
2474
+ query_params={
2475
+ "manufacturer": "$manufacturer",
2476
+ "module_family": "$module_family",
2477
+ "compatible_with_module_bay": ["$parent_module_bay_module", "$parent_module_bay_device"],
2478
+ },
2376
2479
  )
2377
2480
  parent_module_bay_device_filter = DynamicModelChoiceField(
2378
2481
  queryset=Device.objects.all(),
@@ -2418,6 +2521,7 @@ class ModuleForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
2418
2521
  model = Module
2419
2522
  fields = [
2420
2523
  "manufacturer",
2524
+ "module_family",
2421
2525
  "module_type",
2422
2526
  "parent_module_bay",
2423
2527
  "location",
@@ -2433,6 +2537,33 @@ class ModuleForm(LocatableModelFormMixin, NautobotModelForm, TenancyForm):
2433
2537
  "serial": "Module serial number",
2434
2538
  }
2435
2539
 
2540
+ def __init__(self, *args, **kwargs):
2541
+ super().__init__(*args, **kwargs)
2542
+
2543
+ # Handle module_family and manufacturer fields based on parent module bay
2544
+ if self.initial.get("parent_module_bay"):
2545
+ try:
2546
+ parent_bay = ModuleBay.objects.get(pk=self.initial["parent_module_bay"])
2547
+ if parent_bay.module_family:
2548
+ self.fields["module_family"].initial = parent_bay.module_family.id
2549
+ self.fields["module_family"].disabled = True
2550
+ self.fields[
2551
+ "module_family"
2552
+ ].help_text = f"The selected parent module bay requires a module in the {parent_bay.module_family.name} family"
2553
+
2554
+ if parent_bay.requires_first_party_modules:
2555
+ if parent_bay.parent_device:
2556
+ mfr = parent_bay.parent_device.device_type.manufacturer
2557
+ else:
2558
+ mfr = parent_bay.parent_module.module_type.manufacturer
2559
+ self.fields["manufacturer"].initial = mfr.id
2560
+ self.fields["manufacturer"].disabled = True
2561
+ self.fields[
2562
+ "manufacturer"
2563
+ ].help_text = "The selected parent module bay requires first-party modules"
2564
+ except ModuleBay.DoesNotExist:
2565
+ pass
2566
+
2436
2567
  def clean(self):
2437
2568
  cleaned_data = self.cleaned_data
2438
2569
  if cleaned_data["parent_module_bay_device"] and cleaned_data["parent_module_bay_module"]:
@@ -2451,7 +2582,6 @@ class ModuleBulkEditForm(
2451
2582
  StatusModelBulkEditFormMixin,
2452
2583
  RoleModelBulkEditFormMixin,
2453
2584
  NautobotBulkEditForm,
2454
- LocalContextModelBulkEditForm,
2455
2585
  ):
2456
2586
  pk = forms.ModelMultipleChoiceField(queryset=Module.objects.all(), widget=forms.MultipleHiddenInput())
2457
2587
  manufacturer = DynamicModelChoiceField(queryset=Manufacturer.objects.all(), required=False)
@@ -2505,6 +2635,9 @@ class ModuleFilterForm(
2505
2635
  label="Model",
2506
2636
  query_params={"manufacturer": "$manufacturer"},
2507
2637
  )
2638
+ module_family = DynamicModelMultipleChoiceField(
2639
+ queryset=ModuleFamily.objects.all(), to_field_name="name", required=False
2640
+ )
2508
2641
  mac_address = forms.CharField(required=False, label="MAC address")
2509
2642
  has_console_ports = forms.NullBooleanField(
2510
2643
  required=False,
@@ -2564,7 +2697,12 @@ class ModularComponentCreateForm(ModularComponentForm):
2564
2697
  """
2565
2698
 
2566
2699
  device = DynamicModelChoiceField(queryset=Device.objects.all(), required=False)
2567
- module = DynamicModelChoiceField(queryset=Module.objects.all(), required=False)
2700
+ module_family = DynamicModelChoiceField(
2701
+ queryset=ModuleFamily.objects.all(), required=False, help_text="Refine module type by family", label="Family"
2702
+ )
2703
+ module = DynamicModelChoiceField(
2704
+ queryset=Module.objects.all(), required=False, query_params={"module_family": "$module_family"}
2705
+ )
2568
2706
 
2569
2707
 
2570
2708
  class ComponentEditForm(NautobotModelForm):
@@ -2590,7 +2728,12 @@ class ModularComponentEditForm(ComponentEditForm):
2590
2728
  """
2591
2729
 
2592
2730
  device = DynamicModelChoiceField(queryset=Device.objects.all(), required=False)
2593
- module = DynamicModelChoiceField(queryset=Module.objects.all(), required=False)
2731
+ module_family = DynamicModelChoiceField(
2732
+ queryset=ModuleFamily.objects.all(), required=False, help_text="Refine module type by family", label="Family"
2733
+ )
2734
+ module = DynamicModelChoiceField(
2735
+ queryset=Module.objects.all(), required=False, query_params={"module_family": "$module_family"}
2736
+ )
2594
2737
 
2595
2738
  def __init__(self, *args, **kwargs):
2596
2739
  super().__init__(*args, **kwargs)
@@ -2646,6 +2789,7 @@ class ConsolePortCreateForm(ModularComponentCreateForm):
2646
2789
  )
2647
2790
  field_order = (
2648
2791
  "device",
2792
+ "module_family",
2649
2793
  "module",
2650
2794
  "name_pattern",
2651
2795
  "label_pattern",
@@ -2690,6 +2834,7 @@ class ConsoleServerPortForm(ModularComponentEditForm):
2690
2834
  model = ConsoleServerPort
2691
2835
  fields = [
2692
2836
  "device",
2837
+ "module_family",
2693
2838
  "module",
2694
2839
  "name",
2695
2840
  "label",
@@ -2707,6 +2852,7 @@ class ConsoleServerPortCreateForm(ModularComponentCreateForm):
2707
2852
  )
2708
2853
  field_order = (
2709
2854
  "device",
2855
+ "module_family",
2710
2856
  "module",
2711
2857
  "name_pattern",
2712
2858
  "label_pattern",
@@ -2753,6 +2899,7 @@ class PowerPortForm(ModularComponentEditForm):
2753
2899
  model = PowerPort
2754
2900
  fields = [
2755
2901
  "device",
2902
+ "module_family",
2756
2903
  "module",
2757
2904
  "name",
2758
2905
  "label",
@@ -2774,6 +2921,7 @@ class PowerPortCreateForm(ModularComponentCreateForm):
2774
2921
  allocated_draw = forms.IntegerField(min_value=1, required=False, help_text="Allocated draw in watts")
2775
2922
  field_order = (
2776
2923
  "device",
2924
+ "module_family",
2777
2925
  "module",
2778
2926
  "name_pattern",
2779
2927
  "label_pattern",
@@ -2848,6 +2996,7 @@ class PowerOutletForm(ModularComponentEditForm):
2848
2996
  model = PowerOutlet
2849
2997
  fields = [
2850
2998
  "device",
2999
+ "module_family",
2851
3000
  "module",
2852
3001
  "name",
2853
3002
  "label",
@@ -2873,6 +3022,7 @@ class PowerOutletCreateForm(ModularComponentCreateForm):
2873
3022
  feed_leg = forms.ChoiceField(choices=add_blank_choice(PowerOutletFeedLegChoices), required=False)
2874
3023
  field_order = (
2875
3024
  "device",
3025
+ "module_family",
2876
3026
  "module",
2877
3027
  "name_pattern",
2878
3028
  "label_pattern",
@@ -3016,6 +3166,7 @@ class InterfaceForm(InterfaceCommonForm, ModularComponentEditForm):
3016
3166
  model = Interface
3017
3167
  fields = [
3018
3168
  "device",
3169
+ "module_family",
3019
3170
  "module",
3020
3171
  "name",
3021
3172
  "role",
@@ -3155,6 +3306,7 @@ class InterfaceCreateForm(ModularComponentCreateForm, InterfaceCommonForm, RoleN
3155
3306
  )
3156
3307
  field_order = (
3157
3308
  "device",
3309
+ "module_family",
3158
3310
  "module",
3159
3311
  "name_pattern",
3160
3312
  "label_pattern",
@@ -3378,6 +3530,7 @@ class FrontPortForm(ModularComponentEditForm):
3378
3530
  model = FrontPort
3379
3531
  fields = [
3380
3532
  "device",
3533
+ "module_family",
3381
3534
  "module",
3382
3535
  "name",
3383
3536
  "label",
@@ -3405,6 +3558,7 @@ class FrontPortCreateForm(ModularComponentCreateForm):
3405
3558
  )
3406
3559
  field_order = (
3407
3560
  "device",
3561
+ "module_family",
3408
3562
  "module",
3409
3563
  "name_pattern",
3410
3564
  "label_pattern",
@@ -3506,6 +3660,7 @@ class RearPortForm(ModularComponentEditForm):
3506
3660
  model = RearPort
3507
3661
  fields = [
3508
3662
  "device",
3663
+ "module_family",
3509
3664
  "module",
3510
3665
  "name",
3511
3666
  "label",
@@ -3532,6 +3687,7 @@ class RearPortCreateForm(ModularComponentCreateForm):
3532
3687
  )
3533
3688
  field_order = (
3534
3689
  "device",
3690
+ "module_family",
3535
3691
  "module",
3536
3692
  "name_pattern",
3537
3693
  "label_pattern",
@@ -3672,10 +3828,28 @@ class ModuleBayForm(NautobotModelForm):
3672
3828
  required=False,
3673
3829
  label="Parent Device",
3674
3830
  )
3831
+ parent_module_family = DynamicModelChoiceField(
3832
+ queryset=ModuleFamily.objects.all(),
3833
+ required=False,
3834
+ label="Parent Module Family",
3835
+ help_text="Refine parent module by family",
3836
+ )
3675
3837
  parent_module = DynamicModelChoiceField(
3676
3838
  queryset=Module.objects.all(),
3677
3839
  required=False,
3678
3840
  label="Parent Module",
3841
+ query_params={"module_family": "$parent_module_family"},
3842
+ )
3843
+ module_family = DynamicModelChoiceField(
3844
+ queryset=ModuleFamily.objects.all(),
3845
+ required=False,
3846
+ label="Family",
3847
+ help_text="If selected, this bay will only accept module types assigned to this family",
3848
+ )
3849
+ requires_first_party_modules = forms.BooleanField(
3850
+ required=False,
3851
+ label="Requires first-party modules",
3852
+ help_text="This bay will only accept modules from the same manufacturer as the parent device or module",
3679
3853
  )
3680
3854
  # TODO: Installed module field
3681
3855
 
@@ -3683,11 +3857,14 @@ class ModuleBayForm(NautobotModelForm):
3683
3857
  model = ModuleBay
3684
3858
  fields = [
3685
3859
  "parent_device",
3860
+ "parent_module_family",
3686
3861
  "parent_module",
3687
3862
  "name",
3863
+ "module_family",
3688
3864
  "position",
3689
3865
  "label",
3690
3866
  "description",
3867
+ "requires_first_party_modules",
3691
3868
  "tags",
3692
3869
  ]
3693
3870
 
@@ -3697,12 +3874,16 @@ class ModuleBayForm(NautobotModelForm):
3697
3874
  # Disable the parent_device and parent_module fields if an initial value is provided for either
3698
3875
  if "parent_device" in self.initial or "parent_module" in self.initial:
3699
3876
  self.fields["parent_device"].disabled = True
3877
+ self.fields["parent_module_family"].disabled = True
3700
3878
  self.fields["parent_module"].disabled = True
3701
3879
 
3702
3880
 
3703
3881
  class ModuleBayCreateForm(ModuleBayBaseCreateForm):
3704
3882
  parent_device = DynamicModelChoiceField(queryset=Device.objects.all(), required=False)
3705
- parent_module = DynamicModelChoiceField(queryset=Module.objects.all(), required=False)
3883
+ parent_module_family = DynamicModelChoiceField(queryset=ModuleFamily.objects.all(), required=False)
3884
+ parent_module = DynamicModelChoiceField(
3885
+ queryset=Module.objects.all(), required=False, query_params={"module_family": "$parent_module_family"}
3886
+ )
3706
3887
  tags = DynamicModelMultipleChoiceField(
3707
3888
  queryset=Tag.objects.all(),
3708
3889
  required=False,
@@ -3710,10 +3891,13 @@ class ModuleBayCreateForm(ModuleBayBaseCreateForm):
3710
3891
  )
3711
3892
  field_order = (
3712
3893
  "parent_device",
3894
+ "parent_module_family",
3713
3895
  "parent_module",
3714
3896
  "name_pattern",
3715
3897
  "label_pattern",
3716
3898
  "position_pattern",
3899
+ "module_family",
3900
+ "requires_first_party_modules",
3717
3901
  "description",
3718
3902
  "tags",
3719
3903
  )
@@ -3726,8 +3910,7 @@ class ModuleBayBulkCreateForm(
3726
3910
  ):
3727
3911
  pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput())
3728
3912
  description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
3729
-
3730
- field_order = ("name_pattern", "label_pattern", "position_pattern", "description", "tags")
3913
+ field_order = ("name_pattern", "label_pattern", "position_pattern", "description", "module_family", "tags")
3731
3914
 
3732
3915
  class Meta:
3733
3916
  nullable_fields = []
@@ -3746,9 +3929,15 @@ class ModuleBayBulkEditForm(
3746
3929
  NautobotBulkEditForm,
3747
3930
  ):
3748
3931
  pk = forms.ModelMultipleChoiceField(queryset=ModuleBay.objects.all(), widget=forms.MultipleHiddenInput())
3932
+ module_family = DynamicModelChoiceField(
3933
+ queryset=ModuleFamily.objects.all(),
3934
+ required=False,
3935
+ label="Family",
3936
+ help_text="If selected, this bay will only accept module types assigned to this family",
3937
+ )
3749
3938
 
3750
3939
  class Meta:
3751
- nullable_fields = ["label", "description"]
3940
+ nullable_fields = ["label", "description", "module_family"]
3752
3941
 
3753
3942
 
3754
3943
  #
@@ -4164,7 +4353,6 @@ class CableBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, Nau
4164
4353
  class Meta:
4165
4354
  nullable_fields = [
4166
4355
  "type",
4167
- "status",
4168
4356
  "label",
4169
4357
  "color",
4170
4358
  "length",
@@ -4468,7 +4656,7 @@ class PowerPanelBulkEditForm(
4468
4656
 
4469
4657
  class Meta:
4470
4658
  model = PowerPanel
4471
- nullable_fields = ["location", "rack_group"]
4659
+ nullable_fields = ["rack_group"]
4472
4660
 
4473
4661
 
4474
4662
  class PowerPanelFilterForm(NautobotFilterForm, LocatableModelFilterFormMixin):
@@ -4625,9 +4813,7 @@ class DeviceRedundancyGroupFilterForm(NautobotFilterForm, StatusModelFilterFormM
4625
4813
  tags = TagFilterField(model)
4626
4814
 
4627
4815
 
4628
- class DeviceRedundancyGroupBulkEditForm(
4629
- TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm, LocalContextModelBulkEditForm
4630
- ):
4816
+ class DeviceRedundancyGroupBulkEditForm(TagsBulkEditFormMixin, StatusModelBulkEditFormMixin, NautobotBulkEditForm):
4631
4817
  pk = forms.ModelMultipleChoiceField(queryset=DeviceRedundancyGroup.objects.all(), widget=forms.MultipleHiddenInput)
4632
4818
  failover_strategy = forms.ChoiceField(
4633
4819
  choices=add_blank_choice(DeviceRedundancyGroupFailoverStrategyChoices),
@@ -4742,7 +4928,7 @@ class InterfaceRedundancyGroupBulkEditForm(
4742
4928
  queryset=InterfaceRedundancyGroup.objects.all(),
4743
4929
  widget=forms.MultipleHiddenInput,
4744
4930
  )
4745
- protocol = forms.ChoiceField(choices=InterfaceRedundancyGroupProtocolChoices)
4931
+ protocol = forms.ChoiceField(choices=InterfaceRedundancyGroupProtocolChoices, required=False)
4746
4932
  description = forms.CharField(required=False)
4747
4933
  virtual_ip = DynamicModelChoiceField(queryset=IPAddress.objects.all(), required=False)
4748
4934
  secrets_group = DynamicModelChoiceField(queryset=SecretsGroup.objects.all(), required=False)