nautobot 2.2.9__py3-none-any.whl → 2.3.0b1__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 (697) hide show
  1. nautobot/apps/forms.py +4 -0
  2. nautobot/apps/models.py +10 -1
  3. nautobot/circuits/__init__.py +0 -1
  4. nautobot/circuits/apps.py +1 -0
  5. nautobot/circuits/factory.py +15 -3
  6. nautobot/circuits/filters.py +13 -0
  7. nautobot/circuits/forms.py +13 -0
  8. nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
  9. nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
  10. nautobot/circuits/models.py +16 -3
  11. nautobot/circuits/tables.py +16 -2
  12. nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
  13. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
  15. nautobot/circuits/tests/test_api.py +7 -5
  16. nautobot/circuits/tests/test_filters.py +12 -5
  17. nautobot/circuits/tests/test_models.py +33 -2
  18. nautobot/circuits/views.py +2 -3
  19. nautobot/cloud/__init__.py +0 -0
  20. nautobot/cloud/api/__init__.py +0 -0
  21. nautobot/cloud/api/serializers.py +54 -0
  22. nautobot/cloud/api/urls.py +16 -0
  23. nautobot/cloud/api/views.py +48 -0
  24. nautobot/cloud/apps.py +13 -0
  25. nautobot/cloud/factory.py +111 -0
  26. nautobot/cloud/filters.py +184 -0
  27. nautobot/cloud/forms.py +333 -0
  28. nautobot/cloud/homepage.py +43 -0
  29. nautobot/cloud/migrations/0001_initial.py +304 -0
  30. nautobot/cloud/migrations/__init__.py +0 -0
  31. nautobot/cloud/models.py +247 -0
  32. nautobot/cloud/navigation.py +85 -0
  33. nautobot/cloud/tables.py +173 -0
  34. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
  35. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +128 -0
  36. nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
  37. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +100 -0
  38. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +65 -0
  39. nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
  40. nautobot/cloud/tests/__init__.py +0 -0
  41. nautobot/cloud/tests/test_api.py +248 -0
  42. nautobot/cloud/tests/test_filters.py +113 -0
  43. nautobot/cloud/tests/test_models.py +43 -0
  44. nautobot/cloud/tests/test_views.py +153 -0
  45. nautobot/cloud/urls.py +14 -0
  46. nautobot/cloud/views.py +181 -0
  47. nautobot/core/__init__.py +0 -3
  48. nautobot/core/api/metadata.py +1 -0
  49. nautobot/core/api/parsers.py +7 -1
  50. nautobot/core/api/urls.py +1 -0
  51. nautobot/core/api/utils.py +1 -0
  52. nautobot/core/api/views.py +4 -0
  53. nautobot/core/apps/__init__.py +6 -3
  54. nautobot/core/constants.py +8 -0
  55. nautobot/core/factory.py +32 -1
  56. nautobot/core/filters.py +96 -28
  57. nautobot/core/forms/fields.py +10 -4
  58. nautobot/core/forms/forms.py +1 -1
  59. nautobot/core/forms/widgets.py +18 -1
  60. nautobot/core/graphql/generators.py +2 -2
  61. nautobot/core/graphql/schema.py +34 -4
  62. nautobot/core/jobs/__init__.py +17 -6
  63. nautobot/core/jobs/cleanup.py +100 -0
  64. nautobot/core/jobs/groups.py +38 -0
  65. nautobot/core/management/commands/generate_test_data.py +116 -3
  66. nautobot/core/models/__init__.py +34 -9
  67. nautobot/core/models/generics.py +19 -3
  68. nautobot/core/models/name_color_content_types.py +7 -28
  69. nautobot/core/models/querysets.py +4 -3
  70. nautobot/core/models/tree_queries.py +1 -1
  71. nautobot/core/models/utils.py +21 -5
  72. nautobot/core/settings.py +4 -30
  73. nautobot/core/settings.yaml +34 -27
  74. nautobot/core/settings_funcs.py +103 -0
  75. nautobot/core/tables.py +127 -56
  76. nautobot/core/templates/admin/search_form.html +1 -1
  77. nautobot/core/templates/buttons/add.html +11 -3
  78. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
  79. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
  80. nautobot/core/templates/buttons/export.html +101 -53
  81. nautobot/core/templates/buttons/job_import.html +11 -3
  82. nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
  83. nautobot/core/templates/generic/object_bulk_update.html +3 -1
  84. nautobot/core/templates/generic/object_changelog.html +0 -9
  85. nautobot/core/templates/generic/object_list.html +156 -17
  86. nautobot/core/templates/generic/object_retrieve.html +80 -16
  87. nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
  88. nautobot/core/templates/inc/javascript.html +2 -0
  89. nautobot/core/templates/inc/media.html +2 -2
  90. nautobot/core/templates/inc/nav_menu.html +1 -0
  91. nautobot/core/templates/inc/paginator.html +7 -7
  92. nautobot/core/templates/inc/search_panel.html +2 -2
  93. nautobot/core/templates/inc/table.html +2 -2
  94. nautobot/core/templates/nautobot_config.py.j2 +13 -23
  95. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
  96. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
  97. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
  98. nautobot/core/templates/utilities/theme_preview.html +25 -8
  99. nautobot/core/templates/utilities/worker_status.html +152 -0
  100. nautobot/core/templatetags/buttons.py +335 -38
  101. nautobot/core/templatetags/form_helpers.py +1 -1
  102. nautobot/core/templatetags/helpers.py +181 -11
  103. nautobot/core/testing/api.py +5 -4
  104. nautobot/core/testing/filters.py +51 -13
  105. nautobot/core/testing/mixins.py +46 -0
  106. nautobot/core/testing/models.py +22 -0
  107. nautobot/core/testing/schema.py +4 -8
  108. nautobot/core/testing/views.py +31 -14
  109. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  110. nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
  111. nautobot/core/tests/integration/test_swagger.py +1 -1
  112. nautobot/core/tests/nautobot_config.py +0 -1
  113. nautobot/core/tests/runner.py +2 -2
  114. nautobot/core/tests/test_api.py +1 -0
  115. nautobot/core/tests/test_authentication.py +7 -2
  116. nautobot/core/tests/test_filters.py +11 -9
  117. nautobot/core/tests/test_forms.py +9 -0
  118. nautobot/core/tests/test_graphql.py +27 -16
  119. nautobot/core/tests/test_jobs.py +123 -74
  120. nautobot/core/tests/test_tables.py +3 -1
  121. nautobot/core/tests/test_templatetags_helpers.py +12 -5
  122. nautobot/core/tests/test_utils.py +31 -20
  123. nautobot/core/tests/test_views.py +6 -6
  124. nautobot/core/urls.py +8 -3
  125. nautobot/core/utils/deprecation.py +29 -0
  126. nautobot/core/utils/filtering.py +12 -9
  127. nautobot/core/utils/lookup.py +37 -2
  128. nautobot/core/utils/requests.py +4 -1
  129. nautobot/core/views/__init__.py +137 -24
  130. nautobot/core/views/generic.py +118 -66
  131. nautobot/core/views/mixins.py +104 -35
  132. nautobot/core/views/paginator.py +9 -3
  133. nautobot/core/views/renderers.py +121 -56
  134. nautobot/core/views/utils.py +79 -1
  135. nautobot/dcim/__init__.py +0 -1
  136. nautobot/dcim/api/serializers.py +180 -44
  137. nautobot/dcim/api/urls.py +7 -3
  138. nautobot/dcim/api/views.py +53 -7
  139. nautobot/dcim/apps.py +3 -0
  140. nautobot/dcim/choices.py +25 -0
  141. nautobot/dcim/constants.py +7 -0
  142. nautobot/dcim/factory.py +249 -18
  143. nautobot/dcim/filters/__init__.py +369 -193
  144. nautobot/dcim/filters/mixins.py +274 -1
  145. nautobot/dcim/forms.py +817 -109
  146. nautobot/dcim/graphql/types.py +2 -2
  147. nautobot/dcim/homepage.py +1 -1
  148. nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
  149. nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
  150. nautobot/dcim/migrations/0061_module_models.py +861 -0
  151. nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
  152. nautobot/dcim/models/__init__.py +8 -0
  153. nautobot/dcim/models/cables.py +15 -0
  154. nautobot/dcim/models/device_component_templates.py +207 -53
  155. nautobot/dcim/models/device_components.py +275 -106
  156. nautobot/dcim/models/devices.py +466 -13
  157. nautobot/dcim/navigation.py +47 -0
  158. nautobot/dcim/signals.py +3 -3
  159. nautobot/dcim/tables/__init__.py +35 -23
  160. nautobot/dcim/tables/devices.py +231 -59
  161. nautobot/dcim/tables/devicetypes.py +65 -9
  162. nautobot/dcim/tables/racks.py +5 -1
  163. nautobot/dcim/tables/template_code.py +46 -26
  164. nautobot/dcim/templates/dcim/cable_connect.html +76 -3
  165. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
  166. nautobot/dcim/templates/dcim/device/base.html +15 -7
  167. nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
  168. nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
  169. nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
  170. nautobot/dcim/templates/dcim/device/frontports.html +2 -3
  171. nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
  172. nautobot/dcim/templates/dcim/device/inventory.html +2 -3
  173. nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
  174. nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
  175. nautobot/dcim/templates/dcim/device/powerports.html +2 -3
  176. nautobot/dcim/templates/dcim/device/rearports.html +2 -3
  177. nautobot/dcim/templates/dcim/device.html +45 -1
  178. nautobot/dcim/templates/dcim/device_component.html +13 -5
  179. nautobot/dcim/templates/dcim/device_list.html +2 -1
  180. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -6
  181. nautobot/dcim/templates/dcim/devicetype.html +99 -98
  182. nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
  183. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  184. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
  185. nautobot/dcim/templates/dcim/interface.html +17 -2
  186. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
  187. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  188. nautobot/dcim/templates/dcim/manufacturer.html +24 -0
  189. nautobot/dcim/templates/dcim/module/base.html +97 -0
  190. nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
  191. nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
  192. nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
  193. nautobot/dcim/templates/dcim/module_destroy.html +5 -0
  194. nautobot/dcim/templates/dcim/module_frontports.html +53 -0
  195. nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
  196. nautobot/dcim/templates/dcim/module_list.html +20 -0
  197. nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
  198. nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
  199. nautobot/dcim/templates/dcim/module_powerports.html +53 -0
  200. nautobot/dcim/templates/dcim/module_rearports.html +53 -0
  201. nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
  202. nautobot/dcim/templates/dcim/module_update.html +71 -0
  203. nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
  204. nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
  205. nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
  206. nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
  207. nautobot/dcim/templates/dcim/moduletype_retrieve.html +142 -0
  208. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
  209. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
  210. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  211. nautobot/dcim/tests/test_api.py +691 -208
  212. nautobot/dcim/tests/test_filters.py +836 -217
  213. nautobot/dcim/tests/test_models.py +1072 -39
  214. nautobot/dcim/tests/test_views.py +1488 -358
  215. nautobot/dcim/urls.py +17 -2
  216. nautobot/dcim/utils.py +2 -3
  217. nautobot/dcim/views.py +1107 -120
  218. nautobot/extras/__init__.py +0 -1
  219. nautobot/extras/api/serializers.py +115 -3
  220. nautobot/extras/api/urls.py +12 -0
  221. nautobot/extras/api/views.py +125 -7
  222. nautobot/extras/apps.py +2 -2
  223. nautobot/extras/choices.py +43 -0
  224. nautobot/extras/context_managers.py +13 -8
  225. nautobot/extras/datasources/git.py +2 -0
  226. nautobot/extras/factory.py +422 -9
  227. nautobot/extras/filters/__init__.py +174 -3
  228. nautobot/extras/filters/mixins.py +46 -43
  229. nautobot/extras/forms/base.py +17 -4
  230. nautobot/extras/forms/forms.py +227 -8
  231. nautobot/extras/forms/mixins.py +93 -0
  232. nautobot/extras/graphql/types.py +23 -10
  233. nautobot/extras/homepage.py +16 -13
  234. nautobot/extras/jobs.py +2 -2
  235. nautobot/extras/management/__init__.py +1 -0
  236. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
  237. nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
  238. nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
  239. nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
  240. nautobot/extras/migrations/0111_metadata.py +162 -0
  241. nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
  242. nautobot/extras/migrations/0113_saved_views.py +77 -0
  243. nautobot/extras/models/__init__.py +15 -1
  244. nautobot/extras/models/change_logging.py +3 -3
  245. nautobot/extras/models/contacts.py +4 -0
  246. nautobot/extras/models/customfields.py +18 -3
  247. nautobot/extras/models/groups.py +389 -225
  248. nautobot/extras/models/jobs.py +4 -84
  249. nautobot/extras/models/metadata.py +441 -0
  250. nautobot/extras/models/mixins.py +72 -62
  251. nautobot/extras/models/models.py +116 -9
  252. nautobot/extras/models/relationships.py +9 -2
  253. nautobot/extras/models/tags.py +13 -2
  254. nautobot/extras/navigation.py +57 -0
  255. nautobot/extras/plugins/__init__.py +3 -1
  256. nautobot/extras/querysets.py +30 -66
  257. nautobot/extras/signals.py +96 -114
  258. nautobot/extras/tables.py +171 -47
  259. nautobot/extras/templates/extras/dynamicgroup.html +44 -15
  260. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
  261. nautobot/extras/templates/extras/job.html +1 -1
  262. nautobot/extras/templates/extras/job_detail.html +0 -11
  263. nautobot/extras/templates/extras/jobresult.html +61 -74
  264. nautobot/extras/templates/extras/metadatatype_create.html +89 -0
  265. nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
  266. nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
  267. nautobot/extras/templates/extras/objectchange_list.html +0 -12
  268. nautobot/extras/templates/extras/plugins_list.html +1 -3
  269. nautobot/extras/templates/extras/role_retrieve.html +48 -0
  270. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
  271. nautobot/extras/tests/integration/test_customfields.py +1 -0
  272. nautobot/extras/tests/test_api.py +501 -22
  273. nautobot/extras/tests/test_changelog.py +20 -9
  274. nautobot/extras/tests/test_context_managers.py +22 -15
  275. nautobot/extras/tests/test_datasources.py +13 -1
  276. nautobot/extras/tests/test_dynamicgroups.py +201 -171
  277. nautobot/extras/tests/test_filters.py +211 -12
  278. nautobot/extras/tests/test_jobs.py +4 -4
  279. nautobot/extras/tests/test_models.py +499 -4
  280. nautobot/extras/tests/test_relationships.py +1 -0
  281. nautobot/extras/tests/test_views.py +565 -28
  282. nautobot/extras/tests/test_webhooks.py +1 -1
  283. nautobot/extras/urls.py +5 -0
  284. nautobot/extras/utils.py +56 -45
  285. nautobot/extras/views.py +585 -96
  286. nautobot/ipam/__init__.py +0 -1
  287. nautobot/ipam/apps.py +1 -0
  288. nautobot/ipam/factory.py +17 -19
  289. nautobot/ipam/filters.py +14 -1
  290. nautobot/ipam/forms.py +9 -5
  291. nautobot/ipam/graphql/types.py +2 -2
  292. nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
  293. nautobot/ipam/models.py +23 -9
  294. nautobot/ipam/querysets.py +1 -1
  295. nautobot/ipam/signals.py +4 -2
  296. nautobot/ipam/tables.py +1 -0
  297. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
  298. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
  299. nautobot/ipam/templates/ipam/prefix.html +1 -0
  300. nautobot/ipam/tests/test_api.py +37 -18
  301. nautobot/ipam/tests/test_filters.py +26 -2
  302. nautobot/ipam/tests/test_models.py +8 -3
  303. nautobot/ipam/tests/test_querysets.py +1 -1
  304. nautobot/ipam/tests/test_views.py +3 -2
  305. nautobot/ipam/urls.py +2 -2
  306. nautobot/ipam/views.py +25 -28
  307. nautobot/project-static/css/base.css +20 -1
  308. nautobot/project-static/css/dark.css +11 -0
  309. nautobot/project-static/docs/404.html +884 -80
  310. nautobot/project-static/docs/apps/index.html +884 -80
  311. nautobot/project-static/docs/apps/nautobot-apps.html +884 -80
  312. nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
  313. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +911 -112
  314. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +896 -93
  315. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1457 -790
  316. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +927 -136
  317. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +969 -180
  318. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +893 -91
  319. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +889 -85
  320. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +983 -185
  321. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +938 -143
  322. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1064 -274
  323. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1190 -346
  324. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1663 -865
  325. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1156 -373
  326. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2200 -1502
  327. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2229 -1421
  328. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +904 -103
  329. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +955 -155
  330. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1002 -215
  331. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1911 -1275
  332. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1835 -1091
  333. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +896 -93
  334. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2323 -1693
  335. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1785 -1023
  336. nautobot/project-static/docs/development/apps/api/configuration-view.html +884 -80
  337. nautobot/project-static/docs/development/apps/api/database-backend-config.html +884 -80
  338. nautobot/project-static/docs/development/apps/api/models/django-admin.html +884 -80
  339. nautobot/project-static/docs/development/apps/api/models/global-search.html +884 -80
  340. nautobot/project-static/docs/development/apps/api/models/graphql.html +884 -80
  341. nautobot/project-static/docs/development/apps/api/models/index.html +922 -81
  342. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +884 -80
  343. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +884 -80
  344. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +884 -80
  345. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +884 -80
  346. nautobot/project-static/docs/development/apps/api/platform-features/index.html +884 -80
  347. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +884 -80
  348. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +884 -80
  349. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +884 -80
  350. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +884 -80
  351. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +884 -80
  352. nautobot/project-static/docs/development/apps/api/prometheus.html +884 -80
  353. nautobot/project-static/docs/development/apps/api/setup.html +884 -80
  354. nautobot/project-static/docs/development/apps/api/testing.html +884 -80
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +884 -80
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +884 -80
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +884 -80
  358. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +884 -80
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +884 -80
  360. nautobot/project-static/docs/development/apps/api/views/base-template.html +884 -80
  361. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +884 -80
  362. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +884 -80
  363. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +884 -80
  364. nautobot/project-static/docs/development/apps/api/views/index.html +884 -80
  365. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +884 -80
  366. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +884 -80
  367. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +884 -80
  368. nautobot/project-static/docs/development/apps/api/views/notes.html +884 -80
  369. nautobot/project-static/docs/development/apps/api/views/rest-api.html +884 -80
  370. nautobot/project-static/docs/development/apps/api/views/urls.html +884 -80
  371. nautobot/project-static/docs/development/apps/index.html +884 -80
  372. nautobot/project-static/docs/development/apps/migration/code-updates.html +884 -80
  373. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +884 -80
  374. nautobot/project-static/docs/development/apps/migration/from-v1.html +884 -80
  375. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +884 -80
  376. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +884 -80
  377. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +884 -80
  378. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +884 -80
  379. nautobot/project-static/docs/development/apps/porting-from-netbox.html +884 -80
  380. nautobot/project-static/docs/development/core/application-registry.html +884 -80
  381. nautobot/project-static/docs/development/core/best-practices.html +885 -80
  382. nautobot/project-static/docs/development/core/bootstrap-ui.html +884 -80
  383. nautobot/project-static/docs/development/core/caching.html +884 -80
  384. nautobot/project-static/docs/development/core/controllers.html +884 -80
  385. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +884 -80
  386. nautobot/project-static/docs/development/core/generic-views.html +884 -80
  387. nautobot/project-static/docs/development/core/getting-started.html +884 -80
  388. nautobot/project-static/docs/development/core/homepage.html +884 -80
  389. nautobot/project-static/docs/development/core/index.html +884 -91
  390. nautobot/project-static/docs/development/core/model-checklist.html +887 -81
  391. nautobot/project-static/docs/development/core/model-features.html +884 -80
  392. nautobot/project-static/docs/development/core/natural-keys.html +884 -80
  393. nautobot/project-static/docs/development/core/navigation-menu.html +884 -80
  394. nautobot/project-static/docs/development/core/release-checklist.html +887 -83
  395. nautobot/project-static/docs/development/core/role-internals.html +884 -80
  396. nautobot/project-static/docs/development/core/settings.html +884 -80
  397. nautobot/project-static/docs/development/core/style-guide.html +885 -81
  398. nautobot/project-static/docs/development/core/templates.html +896 -81
  399. nautobot/project-static/docs/development/core/testing.html +884 -80
  400. nautobot/project-static/docs/development/core/user-preferences.html +884 -80
  401. nautobot/project-static/docs/development/index.html +884 -80
  402. nautobot/project-static/docs/development/jobs/index.html +1247 -457
  403. nautobot/project-static/docs/development/jobs/migration/from-v1.html +884 -80
  404. nautobot/project-static/docs/index.html +13 -8228
  405. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
  406. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
  407. nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
  408. nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
  409. nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
  410. nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
  411. nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
  412. nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
  413. nautobot/project-static/docs/models/dcim/module.html +15 -0
  414. nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
  415. nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
  416. nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
  417. nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
  418. nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
  419. nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
  420. nautobot/project-static/docs/models/extras/role.html +15 -0
  421. nautobot/project-static/docs/models/extras/savedview.html +15 -0
  422. nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
  423. nautobot/project-static/docs/models/extras/status.html +15 -0
  424. nautobot/project-static/docs/objects.inv +0 -0
  425. nautobot/project-static/docs/overview/application_stack.html +892 -81
  426. nautobot/project-static/docs/overview/design_philosophy.html +886 -82
  427. nautobot/project-static/docs/overview/index.html +9032 -13
  428. nautobot/project-static/docs/release-notes/index.html +887 -83
  429. nautobot/project-static/docs/release-notes/version-1.0.html +884 -80
  430. nautobot/project-static/docs/release-notes/version-1.1.html +884 -80
  431. nautobot/project-static/docs/release-notes/version-1.2.html +884 -80
  432. nautobot/project-static/docs/release-notes/version-1.3.html +884 -80
  433. nautobot/project-static/docs/release-notes/version-1.4.html +884 -80
  434. nautobot/project-static/docs/release-notes/version-1.5.html +885 -81
  435. nautobot/project-static/docs/release-notes/version-1.6.html +885 -81
  436. nautobot/project-static/docs/release-notes/version-2.0.html +884 -80
  437. nautobot/project-static/docs/release-notes/version-2.1.html +884 -80
  438. nautobot/project-static/docs/release-notes/version-2.2.html +990 -323
  439. nautobot/project-static/docs/release-notes/version-2.3.html +9524 -0
  440. nautobot/project-static/docs/requirements.txt +4 -4
  441. nautobot/project-static/docs/search/search_index.json +1 -1
  442. nautobot/project-static/docs/sitemap.xml +335 -260
  443. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  444. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +884 -80
  445. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +884 -80
  446. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +884 -80
  447. nautobot/project-static/docs/user-guide/administration/configuration/index.html +884 -80
  448. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +983 -197
  449. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +884 -80
  450. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +884 -80
  451. nautobot/project-static/docs/user-guide/administration/guides/caching.html +884 -80
  452. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +888 -84
  453. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +884 -80
  454. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +884 -80
  455. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +884 -80
  456. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +884 -80
  457. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +884 -80
  458. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +884 -80
  459. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +884 -80
  460. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +884 -80
  461. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +884 -80
  462. nautobot/project-static/docs/user-guide/administration/installation/index.html +888 -80
  463. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +884 -80
  464. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +884 -80
  465. nautobot/project-static/docs/user-guide/administration/installation/services.html +888 -80
  466. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +900 -91
  467. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +884 -80
  468. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +884 -80
  469. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +884 -80
  470. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +884 -80
  471. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +915 -163
  472. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +884 -80
  473. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +884 -80
  474. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +884 -80
  475. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +884 -80
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +884 -80
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +884 -80
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +884 -80
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +884 -80
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +884 -80
  481. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +884 -80
  482. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +884 -80
  483. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +885 -81
  484. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +884 -80
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +888 -80
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +887 -83
  487. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
  488. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
  494. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +898 -94
  495. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +915 -97
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +915 -97
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +910 -92
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +915 -97
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +898 -94
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +898 -94
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +905 -97
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +912 -108
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +913 -109
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +910 -106
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +898 -94
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +906 -97
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +918 -100
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +928 -110
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +920 -98
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +898 -94
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +929 -111
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +920 -102
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +910 -106
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +913 -109
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +914 -106
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +908 -104
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +898 -94
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +932 -75
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +916 -98
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +898 -94
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +935 -78
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +913 -95
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +921 -117
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +910 -106
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +898 -94
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +914 -96
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +916 -98
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +898 -94
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +898 -94
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +898 -94
  535. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +884 -80
  536. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +884 -80
  537. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +889 -81
  538. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +889 -81
  539. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +884 -80
  540. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +884 -80
  541. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +884 -80
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +884 -80
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +884 -80
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +884 -80
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +884 -80
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +884 -80
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +884 -80
  548. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +884 -80
  549. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +893 -88
  550. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +884 -80
  551. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +884 -80
  552. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +884 -80
  553. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +884 -80
  554. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +884 -80
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +889 -81
  556. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +884 -80
  557. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +884 -80
  558. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +884 -80
  559. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +884 -80
  560. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +884 -80
  561. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +884 -80
  562. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +884 -80
  563. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +884 -80
  564. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +884 -80
  565. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +884 -80
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +884 -80
  567. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +884 -80
  568. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +884 -80
  569. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
  570. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
  571. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
  572. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
  590. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
  591. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
  592. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
  593. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
  594. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +884 -80
  595. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +884 -80
  596. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +884 -80
  597. nautobot/project-static/docs/user-guide/index.html +884 -80
  598. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +884 -80
  599. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +884 -80
  600. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +884 -80
  601. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +884 -80
  602. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1250 -777
  603. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +887 -83
  604. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +884 -80
  605. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +884 -80
  606. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +884 -80
  607. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +884 -80
  608. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +884 -80
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +884 -80
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +887 -83
  611. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +884 -80
  612. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +884 -80
  613. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +888 -80
  614. nautobot/project-static/docs/user-guide/platform-functionality/metadata.html +8948 -0
  615. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +887 -83
  616. nautobot/project-static/docs/user-guide/platform-functionality/note.html +884 -80
  617. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +884 -80
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +884 -80
  619. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +884 -80
  620. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +884 -80
  621. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +884 -80
  622. nautobot/project-static/docs/user-guide/platform-functionality/role.html +887 -83
  623. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
  624. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +887 -83
  625. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
  626. nautobot/project-static/docs/user-guide/platform-functionality/status.html +884 -80
  627. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +884 -80
  628. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +942 -113
  629. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +884 -80
  630. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +884 -80
  631. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +884 -80
  632. nautobot/project-static/js/forms.js +71 -0
  633. nautobot/project-static/js/table_sorting_indicator.js +46 -0
  634. nautobot/project-static/js/tableconfig.js +6 -1
  635. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
  636. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
  637. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
  638. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
  639. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
  640. nautobot/tenancy/__init__.py +0 -1
  641. nautobot/tenancy/apps.py +1 -0
  642. nautobot/tenancy/factory.py +3 -2
  643. nautobot/tenancy/filters/__init__.py +1 -0
  644. nautobot/tenancy/forms.py +1 -1
  645. nautobot/tenancy/templates/tenancy/tenant.html +22 -18
  646. nautobot/tenancy/views.py +11 -10
  647. nautobot/users/__init__.py +0 -1
  648. nautobot/users/api/serializers.py +1 -1
  649. nautobot/users/api/views.py +4 -2
  650. nautobot/users/apps.py +3 -2
  651. nautobot/users/factory.py +3 -3
  652. nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
  653. nautobot/users/models.py +12 -0
  654. nautobot/users/tests/test_filters.py +6 -3
  655. nautobot/users/urls.py +8 -0
  656. nautobot/virtualization/__init__.py +0 -1
  657. nautobot/virtualization/apps.py +1 -0
  658. nautobot/virtualization/filters.py +6 -1
  659. nautobot/virtualization/forms.py +11 -3
  660. nautobot/virtualization/graphql/types.py +2 -2
  661. nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
  662. nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
  663. nautobot/virtualization/tables.py +15 -5
  664. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  665. nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
  666. nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
  667. nautobot/virtualization/tests/test_api.py +9 -4
  668. nautobot/virtualization/tests/test_filters.py +22 -0
  669. nautobot/virtualization/tests/test_models.py +7 -3
  670. nautobot/virtualization/tests/test_views.py +19 -3
  671. nautobot/virtualization/urls.py +2 -2
  672. nautobot/virtualization/views.py +10 -32
  673. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/METADATA +21 -19
  674. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/RECORD +679 -559
  675. nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
  676. nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
  677. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
  678. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
  679. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
  680. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
  681. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
  682. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
  683. nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
  684. nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
  685. nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
  686. nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
  687. nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
  688. nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
  689. nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
  690. nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
  691. nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
  692. nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
  693. /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
  694. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/LICENSE.txt +0 -0
  695. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/NOTICE +0 -0
  696. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/WHEEL +0 -0
  697. {nautobot-2.2.9.dist-info → nautobot-2.3.0b1.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,5 @@
1
+ import re
2
+
1
3
  from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
2
4
  from django.contrib.contenttypes.models import ContentType
3
5
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
@@ -24,7 +26,6 @@ from nautobot.dcim.choices import (
24
26
  PowerOutletFeedLegChoices,
25
27
  PowerOutletTypeChoices,
26
28
  PowerPortTypeChoices,
27
- SubdeviceRoleChoices,
28
29
  )
29
30
  from nautobot.dcim.constants import (
30
31
  NONCONNECTABLE_IFACE_TYPES,
@@ -36,6 +37,7 @@ from nautobot.dcim.constants import (
36
37
  from nautobot.extras.models import (
37
38
  ChangeLoggedModel,
38
39
  RelationshipModel,
40
+ RoleField,
39
41
  Status,
40
42
  StatusField,
41
43
  )
@@ -52,6 +54,7 @@ __all__ = (
52
54
  "InterfaceRedundancyGroup",
53
55
  "InterfaceRedundancyGroupAssociation",
54
56
  "InventoryItem",
57
+ "ModuleBay",
55
58
  "PathEndpoint",
56
59
  "PowerOutlet",
57
60
  "PowerPort",
@@ -70,7 +73,7 @@ class ComponentModel(PrimaryModel):
70
73
  label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Physical label")
71
74
  description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
72
75
 
73
- natural_key_field_names = ["name", "device"]
76
+ natural_key_field_names = ["device", "name"]
74
77
 
75
78
  class Meta:
76
79
  abstract = True
@@ -84,6 +87,9 @@ class ComponentModel(PrimaryModel):
84
87
  """
85
88
  Return a new ObjectChange with the `related_object` pinned to the `device` by default.
86
89
  """
90
+ if "related_object" in kwargs:
91
+ return super().to_objectchange(action, **kwargs)
92
+
87
93
  # Annotate the parent Device
88
94
  try:
89
95
  device = self.device
@@ -93,6 +99,114 @@ class ComponentModel(PrimaryModel):
93
99
 
94
100
  return super().to_objectchange(action, related_object=device, **kwargs)
95
101
 
102
+ @property
103
+ def parent(self):
104
+ return self.device
105
+
106
+
107
+ class ModularComponentModel(ComponentModel):
108
+ device = ForeignKeyWithAutoRelatedName(
109
+ to="dcim.Device",
110
+ on_delete=models.CASCADE,
111
+ blank=True,
112
+ null=True,
113
+ )
114
+ module = ForeignKeyWithAutoRelatedName(
115
+ to="dcim.Module",
116
+ on_delete=models.CASCADE,
117
+ blank=True,
118
+ null=True,
119
+ )
120
+
121
+ natural_key_field_names = ["device", "module", "name"]
122
+
123
+ class Meta:
124
+ abstract = True
125
+ ordering = ("device", "module", "_name")
126
+ constraints = [
127
+ models.UniqueConstraint(
128
+ fields=("device", "name"),
129
+ name="%(app_label)s_%(class)s_device_name_unique",
130
+ ),
131
+ models.UniqueConstraint(
132
+ fields=("module", "name"),
133
+ name="%(app_label)s_%(class)s_module_name_unique",
134
+ ),
135
+ ]
136
+
137
+ @property
138
+ def parent(self):
139
+ """Device that this component belongs to, walking up module inheritance if necessary."""
140
+ return self.module.device if self.module else self.device
141
+
142
+ def render_name_template(self, save=False):
143
+ """
144
+ Replace the {module}, {module.parent}, {module.parent.parent}, etc. variables in the name
145
+ field with the actual module bay positions.
146
+
147
+ Args:
148
+ save (bool, optional): If True, save the object after updating the name field. Defaults to False.
149
+
150
+ If a module bay position is blank, it will be skipped and the parents will be checked until a non-blank
151
+ position is found. If all parent module bays are exhausted, the variable is left as-is.
152
+
153
+ Example:
154
+ - Device (name="Device 1")
155
+ - ModuleBay (position="A")
156
+ - Module
157
+ - ModuleBay (position="")
158
+ - Module
159
+ - Interface (name="{module}{module.parent}")
160
+
161
+ The deeply nested interface would be named "A{module.parent}" after calling this method.
162
+ """
163
+ if self.module and self.module.parent_module_bay and "{module" in self.name:
164
+ name = ""
165
+ module_bay = self.module.parent_module_bay
166
+ positions = []
167
+ while module_bay is not None:
168
+ position = getattr(module_bay, "position", None)
169
+ if position:
170
+ positions.append(position)
171
+ module_bay = getattr(getattr(module_bay, "parent_module", None), "parent_module_bay", None)
172
+ for part in re.split(r"({module(?:\.parent)*})", self.name):
173
+ if re.fullmatch(r"{module(\.parent)*}", part):
174
+ depth = part.count(".parent")
175
+ if depth < len(positions):
176
+ name += positions[depth]
177
+ continue
178
+ name += part
179
+ if self.name != name:
180
+ self.name = name
181
+ if save:
182
+ self.save(update_fields=["_name", "name"])
183
+
184
+ def to_objectchange(self, action, **kwargs):
185
+ """
186
+ Return a new ObjectChange with the `related_object` pinned to the parent `device` or `module`.
187
+ """
188
+ if "related_object" in kwargs:
189
+ return super().to_objectchange(action, **kwargs)
190
+
191
+ # Annotate the parent
192
+ try:
193
+ parent = self.device if self.device else self.module
194
+ except ObjectDoesNotExist:
195
+ # The parent may have already been deleted
196
+ parent = None
197
+
198
+ return super().to_objectchange(action, related_object=parent, **kwargs)
199
+
200
+ def clean(self):
201
+ super().clean()
202
+
203
+ # Validate that a Device or Module is set, but not both
204
+ if self.device and self.module:
205
+ raise ValidationError("Only one of device or module must be set")
206
+
207
+ if not (self.device or self.module):
208
+ raise ValidationError("Either device or module must be set")
209
+
96
210
 
97
211
  class CableTermination(models.Model):
98
212
  """
@@ -212,9 +326,9 @@ class PathEndpoint(models.Model):
212
326
  "graphql",
213
327
  "webhooks",
214
328
  )
215
- class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
329
+ class ConsolePort(ModularComponentModel, CableTermination, PathEndpoint):
216
330
  """
217
- A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
331
+ A physical console port within a Device or Module. ConsolePorts connect to ConsoleServerPorts.
218
332
  """
219
333
 
220
334
  type = models.CharField(
@@ -224,14 +338,6 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
224
338
  help_text="Physical port type",
225
339
  )
226
340
 
227
- class Meta:
228
- ordering = ("device", "_name")
229
- unique_together = ("device", "name")
230
-
231
- @property
232
- def parent(self):
233
- return self.device
234
-
235
341
 
236
342
  #
237
343
  # Console server ports
@@ -239,9 +345,9 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
239
345
 
240
346
 
241
347
  @extras_features("custom_links", "cable_terminations", "custom_validators", "graphql", "webhooks")
242
- class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
348
+ class ConsoleServerPort(ModularComponentModel, CableTermination, PathEndpoint):
243
349
  """
244
- A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
350
+ A physical port within a Device or Module (typically a designated console server) which provides access to ConsolePorts.
245
351
  """
246
352
 
247
353
  type = models.CharField(
@@ -251,14 +357,6 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
251
357
  help_text="Physical port type",
252
358
  )
253
359
 
254
- class Meta:
255
- ordering = ("device", "_name")
256
- unique_together = ("device", "name")
257
-
258
- @property
259
- def parent(self):
260
- return self.device
261
-
262
360
 
263
361
  #
264
362
  # Power ports
@@ -273,9 +371,9 @@ class ConsoleServerPort(CableTermination, PathEndpoint, ComponentModel):
273
371
  "graphql",
274
372
  "webhooks",
275
373
  )
276
- class PowerPort(CableTermination, PathEndpoint, ComponentModel):
374
+ class PowerPort(ModularComponentModel, CableTermination, PathEndpoint):
277
375
  """
278
- A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
376
+ A physical power supply (intake) port within a Device or Module. PowerPorts connect to PowerOutlets.
279
377
  """
280
378
 
281
379
  type = models.CharField(
@@ -297,14 +395,6 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
297
395
  help_text="Allocated power draw (watts)",
298
396
  )
299
397
 
300
- class Meta:
301
- ordering = ("device", "_name")
302
- unique_together = ("device", "name")
303
-
304
- @property
305
- def parent(self):
306
- return self.device
307
-
308
398
  def clean(self):
309
399
  super().clean()
310
400
 
@@ -384,9 +474,9 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
384
474
 
385
475
 
386
476
  @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
387
- class PowerOutlet(CableTermination, PathEndpoint, ComponentModel):
477
+ class PowerOutlet(ModularComponentModel, CableTermination, PathEndpoint):
388
478
  """
389
- A physical power outlet (output) within a Device which provides power to a PowerPort.
479
+ A physical power outlet (output) within a Device or Module which provides power to a PowerPort.
390
480
  """
391
481
 
392
482
  type = models.CharField(
@@ -410,19 +500,11 @@ class PowerOutlet(CableTermination, PathEndpoint, ComponentModel):
410
500
  help_text="Phase (for three-phase feeds)",
411
501
  )
412
502
 
413
- class Meta:
414
- ordering = ("device", "_name")
415
- unique_together = ("device", "name")
416
-
417
- @property
418
- def parent(self):
419
- return self.device
420
-
421
503
  def clean(self):
422
504
  super().clean()
423
505
 
424
506
  # Validate power port assignment
425
- if self.power_port and self.power_port.device != self.device:
507
+ if self.power_port and self.parent and self.power_port.parent != self.parent:
426
508
  raise ValidationError(f"Parent power port ({self.power_port}) must belong to the same device")
427
509
 
428
510
 
@@ -446,6 +528,7 @@ class BaseInterface(RelationshipModel):
446
528
  validators=[MinValueValidator(1), MaxValueValidator(65536)],
447
529
  verbose_name="MTU",
448
530
  )
531
+ role = RoleField(blank=True, null=True)
449
532
  mode = models.CharField(max_length=50, choices=InterfaceModeChoices, blank=True)
450
533
  parent_interface = models.ForeignKey(
451
534
  to="self",
@@ -500,9 +583,9 @@ class BaseInterface(RelationshipModel):
500
583
  "statuses",
501
584
  "webhooks",
502
585
  )
503
- class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
586
+ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInterface):
504
587
  """
505
- A network interface within a Device. A physical Interface can connect to exactly one other Interface.
588
+ A network interface within a Device or Module. A physical Interface can connect to exactly one other Interface.
506
589
  """
507
590
 
508
591
  # Override ComponentModel._name to specify naturalize_interface function
@@ -559,15 +642,14 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
559
642
  verbose_name="IP Addresses",
560
643
  )
561
644
 
562
- class Meta:
563
- ordering = ("device", CollateAsChar("_name"))
564
- unique_together = ("device", "name")
645
+ class Meta(ModularComponentModel.Meta):
646
+ ordering = ("device", "module", CollateAsChar("_name"))
565
647
 
566
648
  def clean(self):
567
649
  super().clean()
568
650
 
569
651
  # VRF validation
570
- if self.vrf and self.vrf not in self.device.vrfs.all():
652
+ if self.vrf and self.parent and self.vrf not in self.parent.vrfs.all():
571
653
  # TODO(jathan): Or maybe we automatically add the VRF to the device?
572
654
  raise ValidationError({"vrf": "VRF must be assigned to same Device."})
573
655
 
@@ -578,19 +660,23 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
578
660
  raise ValidationError({"lag": "A LAG interface cannot be its own parent."})
579
661
 
580
662
  # An interface's LAG must belong to the same device or virtual chassis
581
- if self.lag.device_id != self.device_id:
582
- if self.device.virtual_chassis is None:
663
+ if self.parent and self.lag.parent != self.parent:
664
+ if self.lag.parent is None:
665
+ raise ValidationError(
666
+ {"lag": f"The selected LAG interface ({self.lag}) does not belong to a device."}
667
+ )
668
+ elif self.parent.virtual_chassis is None:
583
669
  raise ValidationError(
584
670
  {
585
- "lag": f"The selected LAG interface ({self.lag}) belongs to a different device ({self.lag.device})."
671
+ "lag": f"The selected LAG interface ({self.lag}) belongs to a different device ({self.lag.parent})."
586
672
  }
587
673
  )
588
- elif self.lag.device.virtual_chassis_id != self.device.virtual_chassis_id:
674
+ elif self.lag.parent.virtual_chassis_id != self.parent.virtual_chassis_id:
589
675
  raise ValidationError(
590
676
  {
591
677
  "lag": (
592
- f"The selected LAG interface ({self.lag}) belongs to {self.lag.device}, which is not part "
593
- f"of virtual chassis {self.device.virtual_chassis}."
678
+ f"The selected LAG interface ({self.lag}) belongs to {self.lag.parent}, which is not part "
679
+ f"of virtual chassis {self.parent.virtual_chassis}."
594
680
  )
595
681
  }
596
682
  )
@@ -623,19 +709,19 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
623
709
  )
624
710
 
625
711
  # An interface's parent must belong to the same device or virtual chassis
626
- if self.parent_interface.device != self.device:
627
- if getattr(self.device, "virtual_chassis", None) is None:
712
+ if self.parent and self.parent_interface.parent != self.parent:
713
+ if getattr(self.parent, "virtual_chassis", None) is None:
628
714
  raise ValidationError(
629
715
  {
630
716
  "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to a different device "
631
- f"({self.parent_interface.device})."
717
+ f"({self.parent_interface.parent})."
632
718
  }
633
719
  )
634
- elif self.parent_interface.device.virtual_chassis != self.device.virtual_chassis:
720
+ elif self.parent_interface.parent.virtual_chassis != self.parent.virtual_chassis:
635
721
  raise ValidationError(
636
722
  {
637
- "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to {self.parent_interface.device}, which "
638
- f"is not part of virtual chassis {self.device.virtual_chassis}."
723
+ "parent_interface": f"The selected parent interface ({self.parent_interface}) belongs to {self.parent_interface.parent}, which "
724
+ f"is not part of virtual chassis {self.parent.virtual_chassis}."
639
725
  }
640
726
  )
641
727
 
@@ -645,7 +731,8 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
645
731
  if (
646
732
  self.untagged_vlan
647
733
  and self.untagged_vlan.locations.exists()
648
- and not self.untagged_vlan.locations.filter(id=self.device.location_id).exists()
734
+ and self.parent
735
+ and not self.untagged_vlan.locations.filter(id=self.parent.location_id).exists()
649
736
  ):
650
737
  raise ValidationError(
651
738
  {
@@ -663,22 +750,22 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
663
750
  raise ValidationError({"bridge": "An interface cannot be bridged to itself."})
664
751
 
665
752
  # A bridged interface belong to the same device or virtual chassis
666
- if self.bridge.device.id != self.device.id:
667
- if getattr(self.device, "virtual_chassis", None) is None:
753
+ if self.parent and self.bridge.parent != self.parent:
754
+ if getattr(self.parent, "virtual_chassis", None) is None:
668
755
  raise ValidationError(
669
756
  {
670
757
  "bridge": (
671
758
  f"The selected bridge interface ({self.bridge}) belongs to a different device "
672
- f"({self.bridge.device})."
759
+ f"({self.bridge.parent})."
673
760
  )
674
761
  }
675
762
  )
676
- elif self.bridge.device.virtual_chassis_id != self.device.virtual_chassis_id:
763
+ elif self.bridge.parent.virtual_chassis_id != self.parent.virtual_chassis_id:
677
764
  raise ValidationError(
678
765
  {
679
766
  "bridge": (
680
- f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.device}, which "
681
- f"is not part of virtual chassis {self.device.virtual_chassis}."
767
+ f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.parent}, which "
768
+ f"is not part of virtual chassis {self.parent.virtual_chassis}."
682
769
  )
683
770
  }
684
771
  )
@@ -766,10 +853,6 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
766
853
  def ip_address_count(self):
767
854
  return self.ip_addresses.count()
768
855
 
769
- @property
770
- def parent(self):
771
- return self.device
772
-
773
856
 
774
857
  @extras_features(
775
858
  "custom_fields",
@@ -879,6 +962,7 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
879
962
  related_name="interface_redundancy_group_associations",
880
963
  )
881
964
  priority = models.PositiveSmallIntegerField()
965
+ is_metadata_associable_model = False
882
966
 
883
967
  class Meta:
884
968
  """Meta class."""
@@ -888,7 +972,7 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
888
972
 
889
973
  def __str__(self):
890
974
  """Return a string representation of the instance."""
891
- return f"{self.interface_redundancy_group}: {self.interface.device} {self.interface}: {self.priority}"
975
+ return f"{self.interface_redundancy_group}: {self.interface.device or self.interface.module} {self.interface}: {self.priority}"
892
976
 
893
977
 
894
978
  #
@@ -897,9 +981,9 @@ class InterfaceRedundancyGroupAssociation(BaseModel, ChangeLoggedModel):
897
981
 
898
982
 
899
983
  @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
900
- class FrontPort(CableTermination, ComponentModel):
984
+ class FrontPort(ModularComponentModel, CableTermination):
901
985
  """
902
- A pass-through port on the front of a Device.
986
+ A pass-through port on the front of a Device or Module.
903
987
  """
904
988
 
905
989
  type = models.CharField(max_length=50, choices=PortTypeChoices)
@@ -912,22 +996,22 @@ class FrontPort(CableTermination, ComponentModel):
912
996
  ],
913
997
  )
914
998
 
915
- class Meta:
916
- ordering = ("device", "_name")
917
- unique_together = (
918
- ("device", "name"),
919
- ("rear_port", "rear_port_position"),
920
- )
999
+ natural_key_field_names = ["device", "module", "name", "rear_port", "rear_port_position"]
921
1000
 
922
- @property
923
- def parent(self):
924
- return self.device
1001
+ class Meta(ModularComponentModel.Meta):
1002
+ constraints = [
1003
+ *ModularComponentModel.Meta.constraints,
1004
+ models.UniqueConstraint(
1005
+ fields=("rear_port", "rear_port_position"),
1006
+ name="dcim_frontport_rear_port_position_unique",
1007
+ ),
1008
+ ]
925
1009
 
926
1010
  def clean(self):
927
1011
  super().clean()
928
1012
 
929
1013
  # Validate rear port assignment
930
- if self.rear_port.device != self.device:
1014
+ if self.parent and self.rear_port.parent != self.parent:
931
1015
  raise ValidationError({"rear_port": f"Rear port ({self.rear_port}) must belong to the same device"})
932
1016
 
933
1017
  # Validate rear port position assignment
@@ -941,9 +1025,9 @@ class FrontPort(CableTermination, ComponentModel):
941
1025
 
942
1026
 
943
1027
  @extras_features("cable_terminations", "custom_links", "custom_validators", "graphql", "webhooks")
944
- class RearPort(CableTermination, ComponentModel):
1028
+ class RearPort(ModularComponentModel, CableTermination):
945
1029
  """
946
- A pass-through port on the rear of a Device.
1030
+ A pass-through port on the rear of a Device or Module.
947
1031
  """
948
1032
 
949
1033
  type = models.CharField(max_length=50, choices=PortTypeChoices)
@@ -955,10 +1039,6 @@ class RearPort(CableTermination, ComponentModel):
955
1039
  ],
956
1040
  )
957
1041
 
958
- class Meta:
959
- ordering = ("device", "_name")
960
- unique_together = ("device", "name")
961
-
962
1042
  def clean(self):
963
1043
  super().clean()
964
1044
 
@@ -972,10 +1052,6 @@ class RearPort(CableTermination, ComponentModel):
972
1052
  }
973
1053
  )
974
1054
 
975
- @property
976
- def parent(self):
977
- return self.device
978
-
979
1055
 
980
1056
  #
981
1057
  # Device bays
@@ -1020,16 +1096,6 @@ class DeviceBay(ComponentModel):
1020
1096
  "installed_device": f"Cannot install the specified device; device is already installed in {current_bay}"
1021
1097
  }
1022
1098
  )
1023
- if self.installed_device.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_CHILD:
1024
- raise ValidationError(
1025
- {
1026
- "installed_device": f'Cannot install device "{self.installed_device}"; device-type "{self.installed_device.device_type}" subdevice_role is not "child".'
1027
- }
1028
- )
1029
-
1030
- @property
1031
- def parent(self):
1032
- return self.device
1033
1099
 
1034
1100
 
1035
1101
  #
@@ -1079,7 +1145,8 @@ class InventoryItem(TreeModel, ComponentModel):
1079
1145
  related_name="inventory_items",
1080
1146
  blank=True,
1081
1147
  null=True,
1082
- verbose_name="The software version installed on this inventory item",
1148
+ verbose_name="Software Version",
1149
+ help_text="The software version installed on this inventory item",
1083
1150
  )
1084
1151
  software_image_files = models.ManyToManyField(
1085
1152
  to="dcim.SoftwareImageFile",
@@ -1101,3 +1168,105 @@ class InventoryItem(TreeModel, ComponentModel):
1101
1168
  For the time being we just use the PK as a natural key.
1102
1169
  """
1103
1170
  return ["pk"]
1171
+
1172
+
1173
+ @extras_features(
1174
+ "custom_links",
1175
+ "custom_validators",
1176
+ "export_templates",
1177
+ "graphql",
1178
+ "webhooks",
1179
+ )
1180
+ class ModuleBay(PrimaryModel):
1181
+ """
1182
+ A slot in a Device or Module which can contain Modules.
1183
+ """
1184
+
1185
+ parent_device = models.ForeignKey(
1186
+ to="dcim.Device",
1187
+ on_delete=models.CASCADE,
1188
+ related_name="module_bays",
1189
+ blank=True,
1190
+ null=True,
1191
+ )
1192
+ parent_module = models.ForeignKey(
1193
+ to="dcim.Module",
1194
+ on_delete=models.CASCADE,
1195
+ related_name="module_bays",
1196
+ blank=True,
1197
+ null=True,
1198
+ )
1199
+ name = models.CharField(max_length=CHARFIELD_MAX_LENGTH, db_index=True)
1200
+ _name = NaturalOrderingField(target_field="name", max_length=CHARFIELD_MAX_LENGTH, blank=True, db_index=True)
1201
+ position = models.CharField(
1202
+ blank=True,
1203
+ max_length=CHARFIELD_MAX_LENGTH,
1204
+ help_text="The position of the module bay within the parent device/module",
1205
+ )
1206
+ label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, help_text="Physical label")
1207
+ description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
1208
+
1209
+ clone_fields = ["parent_device", "parent_module"]
1210
+
1211
+ # The recursive nature of this model combined with the fact that it can be a child of a
1212
+ # device or location makes our natural key implementation unusable, so just use the pk
1213
+ natural_key_field_names = ["pk"]
1214
+
1215
+ class Meta:
1216
+ # TODO: Ordering by parent_module.id is not correct but prevents an infinite loop
1217
+ ordering = (
1218
+ "parent_device",
1219
+ "parent_module__id",
1220
+ "_name",
1221
+ )
1222
+ constraints = [
1223
+ models.UniqueConstraint(
1224
+ fields=["parent_device", "name"],
1225
+ name="dcim_modulebay_parent_device_name_unique",
1226
+ ),
1227
+ models.UniqueConstraint(
1228
+ fields=["parent_module", "name"],
1229
+ name="dcim_modulebay_parent_module_name_unique",
1230
+ ),
1231
+ ]
1232
+
1233
+ @property
1234
+ def parent(self):
1235
+ """Walk up parent chain to find the Device that this ModuleBay is installed in, if one exists."""
1236
+ return self.parent_module.device if self.parent_module else self.parent_device
1237
+
1238
+ def __str__(self):
1239
+ if self.parent_device is not None:
1240
+ return f"{self.parent_device} ({self.name})"
1241
+ else:
1242
+ return f"{self.parent_module} ({self.name})"
1243
+
1244
+ @property
1245
+ def display(self):
1246
+ if self.parent_device is not None:
1247
+ return f"{self.parent_device.display} ({self.name})"
1248
+ else:
1249
+ return f"{self.parent_module.display} ({self.name})"
1250
+
1251
+ def to_objectchange(self, action, **kwargs):
1252
+ """
1253
+ Return a new ObjectChange with the `related_object` pinned to the parent `device` or `module`.
1254
+ """
1255
+ # Annotate the parent
1256
+ try:
1257
+ parent = self.parent
1258
+ except ObjectDoesNotExist:
1259
+ # The parent has already been deleted
1260
+ parent = None
1261
+
1262
+ return super().to_objectchange(action, related_object=parent, **kwargs)
1263
+
1264
+ def clean(self):
1265
+ super().clean()
1266
+
1267
+ # Validate that a Device or Module is set, but not both
1268
+ if self.parent_device and self.parent_module:
1269
+ raise ValidationError("Only one of parent_device or parent_module must be set")
1270
+
1271
+ if not (self.parent_device or self.parent_module):
1272
+ raise ValidationError("Either parent_device or parent_module must be set")