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
@@ -0,0 +1,92 @@
1
+ # Generated by Django 4.2.19 on 2025-03-18 17:22
2
+
3
+ import uuid
4
+
5
+ import django.core.serializers.json
6
+ from django.db import migrations, models
7
+ import django.db.models.deletion
8
+
9
+ import nautobot.core.models.fields
10
+ import nautobot.extras.models.mixins
11
+
12
+
13
+ class Migration(migrations.Migration):
14
+ dependencies = [
15
+ ("extras", "0122_add_graphqlquery_owner_content_type"),
16
+ ("dcim", "0069_softwareimagefile_external_integration"),
17
+ ]
18
+
19
+ operations = [
20
+ migrations.AddField(
21
+ model_name="modulebay",
22
+ name="requires_first_party_modules",
23
+ field=models.BooleanField(default=False),
24
+ ),
25
+ migrations.AddField(
26
+ model_name="modulebaytemplate",
27
+ name="requires_first_party_modules",
28
+ field=models.BooleanField(default=False),
29
+ ),
30
+ migrations.CreateModel(
31
+ name="ModuleFamily",
32
+ fields=[
33
+ (
34
+ "id",
35
+ models.UUIDField(
36
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True
37
+ ),
38
+ ),
39
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
40
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
41
+ (
42
+ "_custom_field_data",
43
+ models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
44
+ ),
45
+ ("name", models.CharField(max_length=255, unique=True)),
46
+ ("description", models.CharField(blank=True, max_length=255)),
47
+ ("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
48
+ ],
49
+ options={
50
+ "verbose_name_plural": "module families",
51
+ "ordering": ["name"],
52
+ },
53
+ bases=(
54
+ nautobot.extras.models.mixins.DynamicGroupMixin,
55
+ nautobot.extras.models.mixins.NotesMixin,
56
+ models.Model,
57
+ ),
58
+ ),
59
+ migrations.AddField(
60
+ model_name="modulebay",
61
+ name="module_family",
62
+ field=models.ForeignKey(
63
+ blank=True,
64
+ null=True,
65
+ on_delete=django.db.models.deletion.PROTECT,
66
+ related_name="module_bays",
67
+ to="dcim.modulefamily",
68
+ ),
69
+ ),
70
+ migrations.AddField(
71
+ model_name="modulebaytemplate",
72
+ name="module_family",
73
+ field=models.ForeignKey(
74
+ blank=True,
75
+ null=True,
76
+ on_delete=django.db.models.deletion.PROTECT,
77
+ related_name="module_bay_templates",
78
+ to="dcim.modulefamily",
79
+ ),
80
+ ),
81
+ migrations.AddField(
82
+ model_name="moduletype",
83
+ name="module_family",
84
+ field=models.ForeignKey(
85
+ blank=True,
86
+ null=True,
87
+ on_delete=django.db.models.deletion.PROTECT,
88
+ related_name="module_types",
89
+ to="dcim.modulefamily",
90
+ ),
91
+ ),
92
+ ]
@@ -38,6 +38,7 @@ from .devices import (
38
38
  InterfaceVDCAssignment,
39
39
  Manufacturer,
40
40
  Module,
41
+ ModuleFamily,
41
42
  ModuleType,
42
43
  Platform,
43
44
  SoftwareImageFile,
@@ -81,6 +82,7 @@ __all__ = (
81
82
  "Module",
82
83
  "ModuleBay",
83
84
  "ModuleBayTemplate",
85
+ "ModuleFamily",
84
86
  "ModuleType",
85
87
  "PathEndpoint",
86
88
  "Platform",
@@ -82,6 +82,8 @@ class ComponentTemplateModel(
82
82
  """
83
83
  raise NotImplementedError()
84
84
 
85
+ instantiate.alters_data = True
86
+
85
87
  def to_objectchange(self, action, **kwargs):
86
88
  """
87
89
  Return a new ObjectChange with the `related_object` pinned to the `device_type` by default.
@@ -122,6 +124,8 @@ class ComponentTemplateModel(
122
124
  **kwargs,
123
125
  )
124
126
 
127
+ instantiate_model.alters_data = True
128
+
125
129
 
126
130
  class ModularComponentTemplateModel(ComponentTemplateModel):
127
131
  """Component Template that supports assignment to a DeviceType or a ModuleType."""
@@ -477,6 +481,18 @@ class ModuleBayTemplate(ModularComponentTemplateModel):
477
481
  )
478
482
  label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Physical label")
479
483
  description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
484
+ module_family = models.ForeignKey(
485
+ to="dcim.ModuleFamily",
486
+ on_delete=models.PROTECT,
487
+ related_name="module_bay_templates",
488
+ blank=True,
489
+ null=True,
490
+ help_text="Module family that can be installed in this bay. Leave blank for no restriction.",
491
+ )
492
+ requires_first_party_modules = models.BooleanField(
493
+ default=False,
494
+ help_text="This bay will only accept modules from the same manufacturer as the parent device or module",
495
+ )
480
496
 
481
497
  natural_key_field_names = ["device_type", "module_type", "name"]
482
498
 
@@ -501,6 +517,8 @@ class ModuleBayTemplate(ModularComponentTemplateModel):
501
517
  position=self.position,
502
518
  label=self.label,
503
519
  description=self.description,
520
+ module_family=self.module_family,
521
+ requires_first_party_modules=self.requires_first_party_modules,
504
522
  _custom_field_data=custom_field_data,
505
523
  )
506
524
 
@@ -182,6 +182,8 @@ class ModularComponentModel(ComponentModel):
182
182
  if save:
183
183
  self.save(update_fields=["_name", "name"])
184
184
 
185
+ render_name_template.alters_data = True
186
+
185
187
  def to_objectchange(self, action, **kwargs):
186
188
  """
187
189
  Return a new ObjectChange with the `related_object` pinned to the parent `device` or `module`.
@@ -820,6 +822,8 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
820
822
  instance.validated_save()
821
823
  return len(ip_addresses)
822
824
 
825
+ add_ip_addresses.alters_data = True
826
+
823
827
  def remove_ip_addresses(self, ip_addresses):
824
828
  """Remove one or more IPAddress instances from this interface's `ip_addresses` many-to-many relationship.
825
829
 
@@ -839,6 +843,8 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
839
843
  count += deleted_count
840
844
  return count
841
845
 
846
+ remove_ip_addresses.alters_data = True
847
+
842
848
  @property
843
849
  def is_connectable(self):
844
850
  return self.type not in NONCONNECTABLE_IFACE_TYPES
@@ -939,6 +945,8 @@ class InterfaceRedundancyGroup(PrimaryModel): # pylint: disable=too-many-ancest
939
945
  )
940
946
  return instance.validated_save()
941
947
 
948
+ add_interface.alters_data = True
949
+
942
950
  def remove_interface(self, interface):
943
951
  """
944
952
  Remove an interface.
@@ -952,6 +960,8 @@ class InterfaceRedundancyGroup(PrimaryModel): # pylint: disable=too-many-ancest
952
960
  )
953
961
  return instance.delete()
954
962
 
963
+ remove_interface.alters_data = True
964
+
955
965
 
956
966
  @extras_features("graphql")
957
967
  class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
@@ -1208,6 +1218,18 @@ class ModuleBay(PrimaryModel):
1208
1218
  blank=True,
1209
1219
  null=True,
1210
1220
  )
1221
+ module_family = models.ForeignKey(
1222
+ to="dcim.ModuleFamily",
1223
+ on_delete=models.PROTECT,
1224
+ related_name="module_bays",
1225
+ blank=True,
1226
+ null=True,
1227
+ help_text="Module family that can be installed in this bay",
1228
+ )
1229
+ requires_first_party_modules = models.BooleanField(
1230
+ default=False,
1231
+ help_text="This bay will only accept modules from the same manufacturer as the parent device or module",
1232
+ )
1211
1233
  name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, db_index=True)
1212
1234
  _name = NaturalOrderingField(target_field="name", max_length=CHARFIELD_MAX_LENGTH, blank=True, db_index=True)
1213
1235
  position = models.CharField(
@@ -1218,7 +1240,7 @@ class ModuleBay(PrimaryModel):
1218
1240
  label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Physical label")
1219
1241
  description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
1220
1242
 
1221
- clone_fields = ["parent_device", "parent_module"]
1243
+ clone_fields = ["parent_device", "parent_module", "module_family", "requires_first_party_modules"]
1222
1244
 
1223
1245
  # The recursive nature of this model combined with the fact that it can be a child of a
1224
1246
  # device or location makes our natural key implementation unusable, so just use the pk
@@ -1287,3 +1309,5 @@ class ModuleBay(PrimaryModel):
1287
1309
 
1288
1310
  if not self.position:
1289
1311
  self.position = self.name
1312
+
1313
+ clean.alters_data = True
@@ -897,6 +897,8 @@ class Device(PrimaryModel, ConfigContextModel):
897
897
  model.objects.bulk_create([x.instantiate(device=self) for x in templates])
898
898
  return instantiated_components
899
899
 
900
+ create_components.alters_data = True
901
+
900
902
  @property
901
903
  def display(self):
902
904
  if self.name:
@@ -1548,6 +1550,30 @@ class ControllerManagedDeviceGroup(TreeModel, PrimaryModel):
1548
1550
  #
1549
1551
 
1550
1552
 
1553
+ @extras_features(
1554
+ "custom_links",
1555
+ "custom_validators",
1556
+ "export_templates",
1557
+ "graphql",
1558
+ "webhooks",
1559
+ )
1560
+ class ModuleFamily(PrimaryModel):
1561
+ """
1562
+ A ModuleFamily represents a classification of ModuleTypes.
1563
+ It is used to enforce compatibility between ModuleBays and Modules.
1564
+ """
1565
+
1566
+ name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, unique=True)
1567
+ description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
1568
+
1569
+ class Meta:
1570
+ ordering = ["name"]
1571
+ verbose_name_plural = "module families"
1572
+
1573
+ def __str__(self):
1574
+ return self.name
1575
+
1576
+
1551
1577
  # TODO: 5840 - Translate comments field from devicetype library, Nautobot doesn't use that field for ModuleType
1552
1578
  @extras_features(
1553
1579
  "custom_links",
@@ -1575,6 +1601,13 @@ class ModuleType(PrimaryModel):
1575
1601
  """
1576
1602
 
1577
1603
  manufacturer = models.ForeignKey(to="dcim.Manufacturer", on_delete=models.PROTECT, related_name="module_types")
1604
+ module_family = models.ForeignKey(
1605
+ to="dcim.ModuleFamily",
1606
+ on_delete=models.PROTECT,
1607
+ related_name="module_types",
1608
+ blank=True,
1609
+ null=True,
1610
+ )
1578
1611
  model = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
1579
1612
  part_number = models.CharField(
1580
1613
  max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Discrete part number (optional)"
@@ -1583,6 +1616,7 @@ class ModuleType(PrimaryModel):
1583
1616
 
1584
1617
  clone_fields = [
1585
1618
  "manufacturer",
1619
+ "module_family",
1586
1620
  ]
1587
1621
 
1588
1622
  class Meta:
@@ -1808,6 +1842,36 @@ class Module(PrimaryModel):
1808
1842
  {"location": f'Modules may not associate to locations of type "{self.location.location_type}".'}
1809
1843
  )
1810
1844
 
1845
+ # Validate module family compatibility
1846
+ if self.parent_module_bay and self.parent_module_bay.module_family:
1847
+ if self.module_type.module_family != self.parent_module_bay.module_family:
1848
+ module_family_name = self.parent_module_bay.module_family.name
1849
+ if self.module_type.module_family is None:
1850
+ module_type_family = "not assigned to a family"
1851
+ else:
1852
+ module_type_family = f"in the family {self.module_type.module_family.name}"
1853
+ raise ValidationError(
1854
+ {
1855
+ "module_type": f"The selected module bay requires a module type in the family {module_family_name}, "
1856
+ f"but the selected module type is {module_type_family}."
1857
+ }
1858
+ )
1859
+
1860
+ # Validate module manufacturer constraint
1861
+ if self.parent_module_bay and self.parent_module_bay.requires_first_party_modules:
1862
+ if self.parent_module_bay.parent_device:
1863
+ parent_mfr = self.parent_module_bay.parent_device.device_type.manufacturer
1864
+ elif self.parent_module_bay.parent_module:
1865
+ parent_mfr = self.parent_module_bay.parent_module.module_type.manufacturer
1866
+ else:
1867
+ parent_mfr = None
1868
+ if parent_mfr and self.module_type.manufacturer != parent_mfr:
1869
+ raise ValidationError(
1870
+ {
1871
+ "module_type": "The selected module bay requires a module type from the same manufacturer as the parent device or module"
1872
+ }
1873
+ )
1874
+
1811
1875
  def save(self, *args, **kwargs):
1812
1876
  is_new = not self.present_in_database
1813
1877
 
@@ -1858,6 +1922,8 @@ class Module(PrimaryModel):
1858
1922
  model.objects.bulk_create([x.instantiate(device=None, module=self) for x in templates])
1859
1923
  return instantiated_components
1860
1924
 
1925
+ create_components.alters_data = True
1926
+
1861
1927
  def render_component_names(self):
1862
1928
  """
1863
1929
  Replace the {module}, {module.parent}, {module.parent.parent}, etc. template variables in descendant
@@ -1882,6 +1948,8 @@ class Module(PrimaryModel):
1882
1948
  for child in self.get_children():
1883
1949
  child.render_component_names()
1884
1950
 
1951
+ render_component_names.alters_data = True
1952
+
1885
1953
  def get_cables(self, pk_list=False):
1886
1954
  """
1887
1955
  Return a QuerySet or PK list matching all Cables connected to any component of this Module.
@@ -293,6 +293,22 @@ menu_items = (
293
293
  ),
294
294
  ),
295
295
  ),
296
+ NavMenuItem(
297
+ link="dcim:modulefamily_list",
298
+ name="Module Families",
299
+ weight=300,
300
+ permissions=[
301
+ "dcim.view_modulefamily",
302
+ ],
303
+ buttons=(
304
+ NavMenuAddButton(
305
+ link="dcim:modulefamily_add",
306
+ permissions=[
307
+ "dcim.add_modulefamily",
308
+ ],
309
+ ),
310
+ ),
311
+ ),
296
312
  ),
297
313
  ),
298
314
  NavMenuGroup(
@@ -30,6 +30,7 @@ from .devices import (
30
30
  InterfaceTable,
31
31
  InventoryItemTable,
32
32
  ModuleBayTable,
33
+ ModuleFamilyTable,
33
34
  ModuleModuleBayTable,
34
35
  ModuleTable,
35
36
  PlatformTable,
@@ -104,6 +105,7 @@ __all__ = (
104
105
  "ManufacturerTable",
105
106
  "ModuleBayTable",
106
107
  "ModuleBayTemplateTable",
108
+ "ModuleFamilyTable",
107
109
  "ModuleModuleBayTable",
108
110
  "ModuleTable",
109
111
  "ModuleTypeTable",
@@ -26,6 +26,7 @@ from nautobot.dcim.models import (
26
26
  InventoryItem,
27
27
  Module,
28
28
  ModuleBay,
29
+ ModuleFamily,
29
30
  Platform,
30
31
  PowerOutlet,
31
32
  PowerPort,
@@ -86,6 +87,7 @@ __all__ = (
86
87
  "InterfaceTable",
87
88
  "InventoryItemTable",
88
89
  "ModuleBayTable",
90
+ "ModuleFamilyTable",
89
91
  "ModuleModuleBayTable",
90
92
  "ModuleTable",
91
93
  "PlatformTable",
@@ -275,6 +277,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
275
277
  )
276
278
  location = tables.Column(linkify=True)
277
279
  tenant = TenantColumn()
280
+ module_type__module_family = tables.Column(linkify=True, verbose_name="Family")
278
281
  tags = TagColumn(url_name="dcim:module_list")
279
282
  actions = ButtonsColumn(Module, prepend_template=MODULE_BUTTONS)
280
283
 
@@ -283,6 +286,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
283
286
  fields = (
284
287
  "pk",
285
288
  "module_type",
289
+ "module_family",
286
290
  "parent_module_bay",
287
291
  "location",
288
292
  "serial",
@@ -296,6 +300,7 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
296
300
  default_columns = (
297
301
  "pk",
298
302
  "module_type",
303
+ "module_family",
299
304
  "parent_module_bay",
300
305
  "location",
301
306
  "serial",
@@ -307,6 +312,40 @@ class ModuleTable(StatusTableMixin, RoleTableMixin, BaseTable):
307
312
  )
308
313
 
309
314
 
315
+ class ModuleFamilyTable(BaseTable):
316
+ pk = ToggleColumn()
317
+ name = tables.Column(linkify=True)
318
+ module_type_count = LinkedCountColumn(
319
+ viewname="dcim:moduletype_list", url_params={"module_family": "pk"}, verbose_name="Module Types"
320
+ )
321
+ module_bay_count = LinkedCountColumn(
322
+ viewname="dcim:modulebay_list", url_params={"module_family": "pk"}, verbose_name="Module Bays"
323
+ )
324
+ tags = TagColumn()
325
+ actions = ButtonsColumn(ModuleFamily)
326
+
327
+ class Meta(BaseTable.Meta):
328
+ model = ModuleFamily
329
+ fields = (
330
+ "pk",
331
+ "name",
332
+ "description",
333
+ "module_type_count",
334
+ "module_bay_count",
335
+ "tags",
336
+ "created",
337
+ "last_updated",
338
+ "actions",
339
+ )
340
+ default_columns = (
341
+ "name",
342
+ "description",
343
+ "module_type_count",
344
+ "module_bay_count",
345
+ "actions",
346
+ )
347
+
348
+
310
349
  #
311
350
  # Device components
312
351
  #
@@ -943,6 +982,7 @@ class ModuleBayTable(BaseTable):
943
982
  installed_module = tables.Column(linkify=True, verbose_name="Installed Module")
944
983
  installed_module__status = ColoredLabelColumn()
945
984
  tags = TagColumn(url_name="dcim:devicebay_list")
985
+ module_family = tables.Column(linkify=True, verbose_name="Family")
946
986
 
947
987
  class Meta(BaseTable.Meta):
948
988
  model = ModuleBay
@@ -953,6 +993,7 @@ class ModuleBayTable(BaseTable):
953
993
  "name",
954
994
  "position",
955
995
  "label",
996
+ "module_family",
956
997
  "description",
957
998
  "installed_module",
958
999
  "installed_module__status",
@@ -965,6 +1006,7 @@ class ModuleBayTable(BaseTable):
965
1006
  "name",
966
1007
  "position",
967
1008
  "label",
1009
+ "module_family",
968
1010
  "description",
969
1011
  "installed_module",
970
1012
  "installed_module__status",
@@ -1008,8 +1050,10 @@ class DeviceModuleBayTable(ModuleBayTable):
1008
1050
  '"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
1009
1051
  attrs={"td": {"class": "text-nowrap"}},
1010
1052
  )
1053
+ module_family = tables.Column(linkify=True, verbose_name="Family")
1011
1054
  installed_module = tables.Column(linkify=True, verbose_name="Installed Module")
1012
1055
  installed_module__status = ColoredLabelColumn(verbose_name="Installed Module Status")
1056
+ requires_first_party_modules = BooleanColumn(verbose_name="First-Party Only")
1013
1057
  actions = ButtonsColumn(model=ModuleBay, buttons=("edit", "delete"), prepend_template=MODULEBAY_BUTTONS)
1014
1058
 
1015
1059
  class Meta(ModularDeviceComponentTable.Meta):
@@ -1018,6 +1062,8 @@ class DeviceModuleBayTable(ModuleBayTable):
1018
1062
  "pk",
1019
1063
  "name",
1020
1064
  "position",
1065
+ "module_family",
1066
+ "requires_first_party_modules",
1021
1067
  "installed_module",
1022
1068
  "installed_module__status",
1023
1069
  "label",
@@ -1029,6 +1075,8 @@ class DeviceModuleBayTable(ModuleBayTable):
1029
1075
  "pk",
1030
1076
  "name",
1031
1077
  "position",
1078
+ "module_family",
1079
+ "requires_first_party_modules",
1032
1080
  "installed_module",
1033
1081
  "installed_module__status",
1034
1082
  "actions",
@@ -158,6 +158,7 @@ class ModuleTypeTable(BaseTable):
158
158
  pk = ToggleColumn()
159
159
  manufacturer = tables.Column(linkify=True)
160
160
  model = tables.Column(linkify=True, verbose_name="Module Type")
161
+ module_family = tables.Column(linkify=True, verbose_name="Family")
161
162
  module_count = LinkedCountColumn(
162
163
  viewname="dcim:module_list",
163
164
  url_params={"module_type": "pk"},
@@ -172,6 +173,7 @@ class ModuleTypeTable(BaseTable):
172
173
  "model",
173
174
  "manufacturer",
174
175
  "part_number",
176
+ "module_family",
175
177
  "module_count",
176
178
  "tags",
177
179
  )
@@ -180,6 +182,7 @@ class ModuleTypeTable(BaseTable):
180
182
  "model",
181
183
  "manufacturer",
182
184
  "part_number",
185
+ "module_family",
183
186
  "module_count",
184
187
  )
185
188
 
@@ -333,8 +336,39 @@ class ModuleBayTemplateTable(ComponentTemplateTable):
333
336
  buttons=("edit", "delete"),
334
337
  return_url_extra=r"%3Ftab=modulebays",
335
338
  )
339
+ module_family = tables.Column(verbose_name="Family", linkify=True)
340
+ requires_first_party_modules = BooleanColumn(verbose_name="Requires First-Party Modules")
336
341
 
337
342
  class Meta(BaseTable.Meta):
338
343
  model = ModuleBayTemplate
339
- fields = ("pk", "name", "position", "label", "description", "actions")
344
+ fields = (
345
+ "pk",
346
+ "name",
347
+ "position",
348
+ "module_family",
349
+ "label",
350
+ "requires_first_party_modules",
351
+ "description",
352
+ "actions",
353
+ )
354
+ default_columns = (
355
+ "pk",
356
+ "name",
357
+ "position",
358
+ "module_family",
359
+ "label",
360
+ "requires_first_party_modules",
361
+ "description",
362
+ "actions",
363
+ )
364
+ field_order = (
365
+ "pk",
366
+ "name",
367
+ "position",
368
+ "module_family",
369
+ "label",
370
+ "requires_first_party_modules",
371
+ "description",
372
+ "actions",
373
+ )
340
374
  empty_text = "None"
@@ -93,7 +93,9 @@ LINKED_RECORD_COUNT = """
93
93
 
94
94
  TREE_LINK = """
95
95
  {% load helpers %}
96
+ {% if not table.hide_hierarchy_ui %}
96
97
  {% tree_hierarchy_ui_representation record.tree_depth|as_range table.hide_hierarchy_ui %}
98
+ {% endif %}
97
99
  <a href="{{ record.get_absolute_url }}">{{ record.name }}</a>
98
100
  """
99
101
 
@@ -1,91 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
- {% block extra_nav_tabs %}
5
- {% if wireless_networks_count %}
6
- <li role="presentation"{% if request.GET.tab == 'wireless_networks' %} class="active"{% endif %}>
7
- <a href="{{ object.get_absolute_url }}#wireless_networks" onclick="switch_tab(this.href, reload=false)" aria-controls="wireless_networks" role="tab" data-toggle="tab">
8
- Wireless Networks {% badge wireless_networks_count %}
9
- </a>
10
- </li>
11
- {% endif %}
12
- {% if radio_profiles_count %}
13
- <li role="presentation"{% if request.GET.tab == 'radio_profiles' %} class="active"{% endif %}>
14
- <a href="{{ object.get_absolute_url }}#radio_profiles" onclick="switch_tab(this.href, reload=false)" aria-controls="radio_profiles" role="tab" data-toggle="tab">
15
- Radio Profiles {% badge radio_profiles_count %}
16
- </a>
17
- </li>
18
- {% endif %}
19
- {% endblock extra_nav_tabs %}
20
-
21
- {% block extra_tab_content %}
22
- {% if wireless_networks_count %}
23
- <div id="wireless_networks" role="tabpanel" class="tab-pane {% if not active_tab and not request.GET.tab or request.GET.tab == "wireless_networks" %}active{% else %}fade{% endif %}">
24
- <div class="panel panel-default">
25
- <div class="panel-heading">
26
- <strong>Wireless Networks</strong>
27
- </div>
28
- {% include 'inc/table.html' with table=wireless_networks_table %}
29
- </div>
30
- {% include 'inc/paginator.html' with paginator=wireless_networks_table.paginator page=wireless_networks_table.page %}
31
- </div>
32
- {% endif %}
33
- {% if radio_profiles_count %}
34
- <div id="radio_profiles" role="tabpanel" class="tab-pane {% if not active_tab and not request.GET.tab or request.GET.tab == "radio_profiles" %}active{% else %}fade{% endif %}">
35
- <div class="panel panel-default">
36
- <div class="panel-heading">
37
- <strong>Radio Profiles</strong>
38
- </div>
39
- {% include 'panel_table.html' with table=radio_profiles_table %}
40
- </div>
41
- {% include 'inc/paginator.html' with paginator=radio_profiles_table.paginator page=radio_profiles_table.page %}
42
- </div>
43
- {% endif %}
44
- {% endblock extra_tab_content %}
45
-
46
- {% block content_left_page %}
47
- <div class="panel panel-default">
48
- <div class="panel-heading">
49
- <strong>Controller</strong>
50
- </div>
51
- <div class="table-responsive">
52
- <table class="table table-hover panel-body attr-table">
53
- <tr>
54
- <td>Name</td>
55
- <td>{{ object.name|placeholder }}</td>
56
- </tr>
57
- <tr>
58
- <td>Controller</td>
59
- <td>{{ object.controller|hyperlinked_object }}</td>
60
- </tr>
61
- <tr>
62
- <td>Parent</td>
63
- <td>{{ object.parent|hyperlinked_object }}</td>
64
- </tr>
65
- <tr>
66
- <td>Capabilities</td>
67
- <td>{{ object.get_capabilities_display }}</td>
68
- </tr>
69
- <tr>
70
- <td>Weight</td>
71
- <td>{{ object.weight }}</td>
72
- </tr>
73
- {% include 'inc/tenant_table_row.html' %}
74
- <tr>
75
- <td>Description</td>
76
- <td>{{ object.description|placeholder }}</td>
77
- </tr>
78
- </table>
79
- </div>
80
- </div>
81
- {% endblock content_left_page %}
82
-
83
- {% block content_full_width_page %}
84
- <div class="panel panel-default">
85
- <div class="panel-heading">
86
- <strong>Devices</strong>
87
- </div>
88
- {% include 'responsive_table.html' with table=devices_table %}
89
- </div>
90
- {% include 'inc/paginator.html' with paginator=devices_table.paginator page=devices_table.page %}
91
- {% endblock content_full_width_page %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}